From d4fe6cd2bfee50cfcde5de5c3f58240552e9c71d Mon Sep 17 00:00:00 2001 From: David Celis Date: Sun, 9 Apr 2017 09:33:40 -0700 Subject: [PATCH] Allow users to update their Account in the API (#1179) * Allow users to update their Account in the API It would be nice for API clients to be able to allow users to update their accounts without having to wrap Mastodon in a web view. This patch adds an API endpoint to let users submit a PATCH for their account. Signed-off-by: David Celis * Add /api/v1/accounts/update_credentials to the API docs Signed-off-by: David Celis --- app/controllers/api/v1/accounts_controller.rb | 17 ++++++++-- config/routes.rb | 1 + docs/Using-the-API/API.md | 11 +++++++ .../api/v1/accounts_controller_spec.rb | 33 +++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 454873116a..bb7cda0355 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true class Api::V1::AccountsController < ApiController - before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute] + before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute, :update_credentials] before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute] + before_action -> { doorkeeper_authorize! :write }, only: [:update_credentials] before_action :require_user!, except: [:show, :following, :followers, :statuses] - before_action :set_account, except: [:verify_credentials, :suggestions, :search] + before_action :set_account, except: [:verify_credentials, :update_credentials, :suggestions, :search] respond_to :json @@ -15,6 +16,14 @@ class Api::V1::AccountsController < ApiController render action: :show end + def update_credentials + @account = current_user.account + + @account.update_attributes!(account_params) + + render action: :show + end + def following results = Follow.where(account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]) accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h @@ -135,4 +144,8 @@ class Api::V1::AccountsController < ApiController def statuses_pagination_params(core_params) params.permit(:limit, :only_media, :exclude_replies).merge(core_params) end + + def account_params + @account_params ||= params.permit(:display_name, :note, :avatar, :header) + end end diff --git a/config/routes.rb b/config/routes.rb index b0a13aa78a..9adcdb862b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -163,6 +163,7 @@ Rails.application.routes.draw do collection do get :relationships get :verify_credentials + patch :update_credentials get :search end diff --git a/docs/Using-the-API/API.md b/docs/Using-the-API/API.md index 8a648b6d09..2b7081470d 100644 --- a/docs/Using-the-API/API.md +++ b/docs/Using-the-API/API.md @@ -85,6 +85,17 @@ Returns an [Account](#account). Returns the authenticated user's [Account](#account). +#### Updating the current user: + + PATCH /api/v1/accounts/update_credentials + +Form data: + +- `display_name`: The name to display in the user's profile +- `note`: A new biography for the user +- `avatar`: A base64 encoded image to display as the user's avatar (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`) +- `header`: A base64 encoded image to display as the user's header image (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`) + #### Getting an account's followers: GET /api/v1/accounts/:id/followers diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb index 5d36b01591..98c87bf16f 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts_controller_spec.rb @@ -24,6 +24,39 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end end + describe 'PATCH #update_credentials' do + it 'returns http success' do + expect(user.account.avatar).not_to exist + expect(user.account.header).not_to exist + + avatar = File.read(Rails.root.join('app', 'assets', 'images', 'logo.png')) + header = File.read(Rails.root.join('app', 'assets', 'images', 'mastodon-getting-started.png')) + params = { + display_name: "Alice Isn't Dead", + note: "Hi!\n\nToot toot!", + avatar: "data:image/png;base64,#{Base64.encode64(avatar)}", + header: "data:image/png;base64,#{Base64.encode64(header)}" + } + patch :update_credentials, params: params + expect(response).to have_http_status(:success) + + user.reload + + expect(user.account.display_name).to eq("Alice Isn't Dead") + expect(user.account.note).to eq("Hi!\n\nToot toot!") + expect(user.account.avatar).to exist + expect(user.account.header).to exist + end + + it 'respects Account validations' do + note = "This is too long. " * 10 + error = { error: 'The account could not be updated: Note is too long (maximum is 160 characters)' }.to_json + patch :update_credentials, params: { note: note } + expect(response).to have_http_status(:unprocessable_entity) + expect(response.body).to eq(error) + end + end + describe 'GET #statuses' do it 'returns http success' do get :statuses, params: { id: user.account.id }