Commit 3845276c authored by Aishwarya Subramanian's avatar Aishwarya Subramanian Committed by Jarka Košanová

Added user job title to users table

Schema changes for new column
Controller changes and specs
Added relevant specs
Added changelog entry
Added few api documentation changes
parent 2b5d9cb7
...@@ -117,6 +117,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -117,6 +117,7 @@ class ProfilesController < Profiles::ApplicationController
:private_profile, :private_profile,
:include_private_contributions, :include_private_contributions,
:timezone, :timezone,
:job_title,
status: [:emoji, :message] status: [:emoji, :message]
) )
end end
......
...@@ -162,6 +162,7 @@ class User < ApplicationRecord ...@@ -162,6 +162,7 @@ class User < ApplicationRecord
has_one :status, class_name: 'UserStatus' has_one :status, class_name: 'UserStatus'
has_one :user_preference has_one :user_preference
has_one :user_detail
# #
# Validations # Validations
...@@ -259,8 +260,10 @@ class User < ApplicationRecord ...@@ -259,8 +260,10 @@ class User < ApplicationRecord
delegate :sourcegraph_enabled, :sourcegraph_enabled=, to: :user_preference delegate :sourcegraph_enabled, :sourcegraph_enabled=, to: :user_preference
delegate :setup_for_company, :setup_for_company=, to: :user_preference delegate :setup_for_company, :setup_for_company=, to: :user_preference
delegate :render_whitespace_in_code, :render_whitespace_in_code=, to: :user_preference delegate :render_whitespace_in_code, :render_whitespace_in_code=, to: :user_preference
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
accepts_nested_attributes_for :user_preference, update_only: true accepts_nested_attributes_for :user_preference, update_only: true
accepts_nested_attributes_for :user_detail, update_only: true
state_machine :state, initial: :active do state_machine :state, initial: :active do
event :block do event :block do
...@@ -1619,6 +1622,10 @@ class User < ApplicationRecord ...@@ -1619,6 +1622,10 @@ class User < ApplicationRecord
super.presence || build_user_preference super.presence || build_user_preference
end end
def user_detail
super.presence || build_user_detail
end
def todos_limited_to(ids) def todos_limited_to(ids)
todos.where(id: ids) todos.where(id: ids)
end end
......
# frozen_string_literal: true
class UserDetail < ApplicationRecord
belongs_to :user
validates :job_title, presence: true, length: { maximum: 200 }
end
---
title: Add support for user Job Title
merge_request: 25483
author:
type: added
...@@ -20,6 +20,8 @@ en: ...@@ -20,6 +20,8 @@ en:
token: "Grafana HTTP API Token" token: "Grafana HTTP API Token"
grafana_url: "Grafana API URL" grafana_url: "Grafana API URL"
grafana_enabled: "Grafana integration enabled" grafana_enabled: "Grafana integration enabled"
user/user_detail:
job_title: 'Job title'
views: views:
pagination: pagination:
previous: "Prev" previous: "Prev"
......
# frozen_string_literal: true
class CreateUserDetails < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
with_lock_retries do
create_table :user_details, id: false do |t|
t.references :user, index: false, foreign_key: { on_delete: :cascade }, null: false, primary_key: true
t.string :job_title, limit: 200, default: "", null: false
end
end
add_index :user_details, :user_id, unique: true
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_02_26_162723) do ActiveRecord::Schema.define(version: 2020_02_27_165129) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -4170,6 +4170,11 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do ...@@ -4170,6 +4170,11 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do
t.index ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true t.index ["user_id", "key"], name: "index_user_custom_attributes_on_user_id_and_key", unique: true
end end
create_table "user_details", primary_key: "user_id", force: :cascade do |t|
t.string "job_title", limit: 200, default: "", null: false
t.index ["user_id"], name: "index_user_details_on_user_id", unique: true
end
create_table "user_interacted_projects", id: false, force: :cascade do |t| create_table "user_interacted_projects", id: false, force: :cascade do |t|
t.integer "user_id", null: false t.integer "user_id", null: false
t.integer "project_id", null: false t.integer "project_id", null: false
...@@ -5028,6 +5033,7 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do ...@@ -5028,6 +5033,7 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do
add_foreign_key "u2f_registrations", "users" add_foreign_key "u2f_registrations", "users"
add_foreign_key "user_callouts", "users", on_delete: :cascade add_foreign_key "user_callouts", "users", on_delete: :cascade
add_foreign_key "user_custom_attributes", "users", on_delete: :cascade add_foreign_key "user_custom_attributes", "users", on_delete: :cascade
add_foreign_key "user_details", "users", on_delete: :cascade
add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade add_foreign_key "user_interacted_projects", "projects", name: "fk_722ceba4f7", on_delete: :cascade
add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade add_foreign_key "user_interacted_projects", "users", name: "fk_0894651f08", on_delete: :cascade
add_foreign_key "user_preferences", "users", on_delete: :cascade add_foreign_key "user_preferences", "users", on_delete: :cascade
......
...@@ -95,6 +95,7 @@ GET /users ...@@ -95,6 +95,7 @@ GET /users
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"organization": "", "organization": "",
"job_title": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1, "theme_id": 1,
...@@ -132,6 +133,7 @@ GET /users ...@@ -132,6 +133,7 @@ GET /users
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"organization": "", "organization": "",
"job_title": "",
"last_sign_in_at": null, "last_sign_in_at": null,
"confirmed_at": "2012-05-30T16:53:06.148Z", "confirmed_at": "2012-05-30T16:53:06.148Z",
"theme_id": 1, "theme_id": 1,
...@@ -247,7 +249,8 @@ Parameters: ...@@ -247,7 +249,8 @@ Parameters:
"linkedin": "", "linkedin": "",
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"organization": "" "organization": "",
"job_title": "Operations Specialist"
} }
``` ```
...@@ -282,6 +285,7 @@ Example Responses: ...@@ -282,6 +285,7 @@ Example Responses:
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"organization": "", "organization": "",
"job_title": "Operations Specialist",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1, "theme_id": 1,
...@@ -545,6 +549,7 @@ GET /user ...@@ -545,6 +549,7 @@ GET /user
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"organization": "", "organization": "",
"job_title": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1, "theme_id": 1,
......
...@@ -4,7 +4,7 @@ module API ...@@ -4,7 +4,7 @@ module API
module Entities module Entities
class User < UserBasic class User < UserBasic
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization, :job_title
end end
end end
end end
...@@ -89,6 +89,16 @@ describe ProfilesController, :request_store do ...@@ -89,6 +89,16 @@ describe ProfilesController, :request_store do
expect(user.reload.status.message).to eq('Working hard!') expect(user.reload.status.message).to eq('Working hard!')
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:found)
end end
it 'allows updating user specified job title' do
title = 'Marketing Executive'
sign_in(user)
put :update, params: { user: { job_title: title } }
expect(user.reload.job_title).to eq(title)
expect(response).to have_gitlab_http_status(:found)
end
end end
describe 'PUT update_username' do describe 'PUT update_username' do
......
# frozen_string_literal: true
FactoryBot.define do
factory :user_detail do
user
job_title { 'VP of Sales' }
end
end
# frozen_string_literal: true
require 'spec_helper'
describe UserDetail do
it { is_expected.to belong_to(:user) }
describe 'validations' do
describe 'job_title' do
it { is_expected.to validate_presence_of(:job_title) }
it { is_expected.to validate_length_of(:job_title).is_at_most(200) }
end
end
end
...@@ -29,6 +29,7 @@ describe User, :do_not_mock_admin_mode do ...@@ -29,6 +29,7 @@ describe User, :do_not_mock_admin_mode do
it { is_expected.to have_one(:namespace) } it { is_expected.to have_one(:namespace) }
it { is_expected.to have_one(:status) } it { is_expected.to have_one(:status) }
it { is_expected.to have_one(:max_access_level_membership) } it { is_expected.to have_one(:max_access_level_membership) }
it { is_expected.to have_one(:user_detail) }
it { is_expected.to have_many(:snippets).dependent(:destroy) } it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) } it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:project_members) } it { is_expected.to have_many(:project_members) }
...@@ -4318,4 +4319,19 @@ describe User, :do_not_mock_admin_mode do ...@@ -4318,4 +4319,19 @@ describe User, :do_not_mock_admin_mode do
expect(user.hook_attrs).to eq(user_attributes) expect(user.hook_attrs).to eq(user_attributes)
end end
end end
describe 'user detail' do
context 'when user is initialized' do
let(:user) { build(:user) }
it { expect(user.user_detail).to be_present }
it { expect(user.user_detail).not_to be_persisted }
end
context 'when user detail exists' do
let(:user) { create(:user, job_title: 'Engineer') }
it { expect(user.user_detail).to be_persisted }
end
end
end end
...@@ -330,6 +330,21 @@ describe API::Users, :do_not_mock_admin_mode do ...@@ -330,6 +330,21 @@ describe API::Users, :do_not_mock_admin_mode do
expect(json_response.keys).not_to include 'last_sign_in_ip' expect(json_response.keys).not_to include 'last_sign_in_ip'
end end
context 'when job title is present' do
let(:job_title) { 'Fullstack Engineer' }
before do
create(:user_detail, user: user, job_title: job_title)
end
it 'returns job title of a user' do
get api("/users/#{user.id}", user)
expect(response).to match_response_schema('public_api/v4/user/basic')
expect(json_response['job_title']).to eq(job_title)
end
end
context 'when authenticated as admin' do context 'when authenticated as admin' do
it 'includes the `is_admin` field' do it 'includes the `is_admin` field' do
get api("/users/#{user.id}", admin) get api("/users/#{user.id}", admin)
......
...@@ -64,6 +64,13 @@ describe Users::UpdateService do ...@@ -64,6 +64,13 @@ describe Users::UpdateService do
end.not_to change { user.name } end.not_to change { user.name }
end end
it 'updates user detail with provided attributes' do
result = update_user(user, job_title: 'Backend Engineer')
expect(result).to eq(status: :success)
expect(user.job_title).to eq('Backend Engineer')
end
def update_user(user, opts) def update_user(user, opts)
described_class.new(user, opts.merge(user: user)).execute described_class.new(user, opts.merge(user: user)).execute
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment