Commit 2076dca8 authored by Gary Holtz's avatar Gary Holtz

Add a User/Preferences API with update service

* Adds documentation surrounding the new parameter
* Reveals the parameter so frontend is able to access it
* Adds a very basic update service to run inside the endpoint
parent a2ab3c5d
......@@ -110,6 +110,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs
@file_by_file_default = current_user&.view_diffs_file_by_file
@coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports?
@update_current_user_path = expose_path(api_v4_user_preferences_path)
@endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request)
set_pipeline_variables
......
# frozen_string_literal: true
module UserPreferences
class UpdateService < BaseService
def initialize(user, params = {})
@preferences = user.user_preference
@params = params.to_h.dup.with_indifferent_access
end
def execute
if @preferences.update(@params)
ServiceResponse.success(
message: 'Preference was updated',
payload: { preferences: @preferences })
else
ServiceResponse.error(message: 'Could not update preference')
end
end
end
end
......@@ -88,6 +88,7 @@
endpoint_coverage: @coverage_path,
help_page_path: suggest_changes_help_path,
current_user_data: @current_user_data,
update_current_user_path: @update_current_user_path,
project_path: project_path(@merge_request.project),
changes_empty_state_illustration: image_path('illustrations/merge_request_changes_empty.svg'),
is_fluid_layout: fluid_layout.to_s,
......
---
title: Create UserPreferences API
merge_request: 55033
author:
type: added
......@@ -677,6 +677,28 @@ Example response:
}
```
## User preference modification
Update the current user's preferences.
```plaintext
PUT /user/preferences
```
```json
{
"id": 1,
"user_id": 1
"view_diffs_file_by_file": true
}
```
Parameters:
| Attribute | Required | Description |
| :--------------------------- | :------- | :---------------------------------------------------------- |
| `view_diffs_file_by_file` | Yes | Flag indicating the user sees only one file diff per page. |
## Set user status
Set the status of the current user.
......
# frozen_string_literal: true
module API
module Entities
class UserPreferences < Grape::Entity
expose :id, :user_id, :view_diffs_file_by_file
end
end
end
......@@ -998,6 +998,29 @@ module API
present paginate(current_user.emails), with: Entities::Email
end
desc "Update the current user's preferences" do
success Entities::UserPreferences
detail 'This feature was introduced in GitLab 13.10.'
end
params do
requires :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page'
end
put "preferences", feature_category: :users do
authenticate!
preferences = current_user.user_preference
attrs = declared_params(include_missing: false)
service = ::UserPreferences::UpdateService.new(current_user, attrs).execute
if service.success?
present preferences, with: Entities::UserPreferences
else
render_api_error!('400 Bad Request', 400)
end
end
desc 'Get a single email address owned by the currently authenticated user' do
success Entities::Email
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Users do
let_it_be(:user) { create(:user) }
describe 'PUT /user/preferences/' do
context "with correct attributes and a logged in user" do
it 'returns a success status and the value has been changed' do
put api("/user/preferences", user), params: { view_diffs_file_by_file: true }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['view_diffs_file_by_file']).to eq(true)
expect(user.reload.view_diffs_file_by_file).to be_truthy
end
end
context "missing a preference" do
it 'returns a bad request status' do
put api("/user/preferences", user), params: {}
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context "without a logged in user" do
it 'returns an unauthorized status' do
put api("/user/preferences"), params: { view_diffs_file_by_file: true }
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context "with an unsupported preference" do
it 'returns a bad parameter' do
put api("/user/preferences", user), params: { jawn: true }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context "with an unsupported value" do
it 'returns a bad parameter' do
put api("/user/preferences", user), params: { view_diffs_file_by_file: 3 }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context "with an update service failure" do
it 'returns a bad request' do
bad_service = double("Failed Service", success?: false)
allow_next_instance_of(::UserPreferences::UpdateService) do |instance|
allow(instance).to receive(:execute).and_return(bad_service)
end
put api("/user/preferences", user), params: { view_diffs_file_by_file: true }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe UserPreferences::UpdateService do
let(:user) { create(:user) }
let(:params) { { view_diffs_file_by_file: false } }
describe '#execute' do
subject(:service) { described_class.new(user, params) }
context 'successfully updating the record' do
it 'updates the preference and returns a success' do
result = service.execute
expect(result.status).to eq(:success)
expect(result.payload[:preferences].view_diffs_file_by_file).to eq(params[:view_diffs_file_by_file])
end
end
context 'unsuccessfully updating the record' do
before do
allow(user.user_preference).to receive(:update).and_return(false)
end
it 'returns an error' do
result = service.execute
expect(result.status).to eq(:error)
end
end
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