Add API portion of account status

Adds PutStatus.java and GetStatus.java for modification of the logged-in
user's status field.

Feature: Issue 4394
Change-Id: Id13241dcd98c46101cea2f2337c1fb731d0465a0
This commit is contained in:
Kasper Nilsson 2017-01-27 13:51:07 -08:00
parent f3837bd5df
commit eb64a0ae1e
12 changed files with 239 additions and 1 deletions

View File

@ -285,6 +285,66 @@ Deletes the name of an account.
HTTP/1.1 204 No Content
----
[[get-account-status]]
=== Get Account Status
--
'GET /accounts/link:#account-id[\{account-id\}]/status'
--
Retrieves the status of an account.
.Request
----
GET /accounts/self/status HTTP/1.0
----
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json; charset=UTF-8
)]}'
"Available"
----
If the account does not have a status an empty string is returned.
[[set-account-status]]
=== Set Account Status
--
'PUT /accounts/link:#account-id[\{account-id\}]/status'
--
Sets the status of an account.
The new account status must be provided in the request body inside
an link:#account-status-input[AccountStatusInput] entity.
.Request
----
PUT /accounts/self/status HTTP/1.0
Content-Type: application/json; charset=UTF-8
{
"status": "Out Of Office"
}
----
As response the new account status is returned.
.Response
----
HTTP/1.1 200 OK
Content-Disposition: attachment
Content-Type: application/json; charset=UTF-8
)]}'
"Out Of Office"
----
If the name was deleted the response is "`204 No Content`".
[[get-username]]
=== Get Username
--
@ -2173,6 +2233,18 @@ for an account.
If not set or if set to an empty string, the account name is deleted.
|=============================
[[account-status-input]]
=== AccountStatusInput
The `AccountStatusInput` entity contains information for setting a status
for an account.
[options="header",cols="1,^2,4"]
|=============================
|Field Name ||Description
|`status` |optional|The new status of the account. +
If not set or if set to an empty string, the account status is deleted.
|=============================
[[capability-info]]
=== CapabilityInfo
The `CapabilityInfo` entity contains information about the global

View File

@ -51,6 +51,7 @@ public class TestAccount {
public final String fullName;
public final KeyPair sshKey;
public final String httpPassword;
public String status;
TestAccount(Account.Id id, String username, String email, String fullName,
KeyPair sshKey, String httpPassword) {

View File

@ -421,6 +421,19 @@ public class AccountIT extends AbstractDaemonTest {
}
}
@Test
public void putStatus() throws Exception {
List<String> statuses = ImmutableList.of(
"OOO", "Busy");
AccountInfo info;
for (String status : statuses) {
gApi.accounts().self().setStatus(status);
admin.status = status;
info = gApi.accounts().self().get();
assertUser(info, admin);
}
}
@Test
public void addInvalidEmail() throws Exception {
List<String> emails = ImmutableList.of(
@ -877,5 +890,6 @@ public class AccountIT extends AbstractDaemonTest {
assertThat(info.name).isEqualTo(account.fullName);
assertThat(info.email).isEqualTo(account.email);
assertThat(info.username).isEqualTo(account.username);
assertThat(info.status).isEqualTo(account.status);
}
}

View File

@ -66,6 +66,8 @@ public interface AccountApi {
void addEmail(EmailInput input) throws RestApiException;
void setStatus(String status) throws RestApiException;
List<SshKeyInfo> listSshKeys() throws RestApiException;
SshKeyInfo addSshKey(String key) throws RestApiException;
void deleteSshKey(int seq) throws RestApiException;
@ -184,6 +186,11 @@ public interface AccountApi {
throw new NotImplementedException();
}
@Override
public void setStatus(String status) {
throw new NotImplementedException();
}
@Override
public List<SshKeyInfo> listSshKeys() {
throw new NotImplementedException();

View File

@ -24,6 +24,7 @@ public class AccountInfo {
public String username;
public List<AvatarInfo> avatars;
public Boolean _moreAccounts;
public String status;
public AccountInfo(Integer id) {
this._accountId = id;

View File

@ -42,7 +42,10 @@ public abstract class AccountDirectory {
USERNAME,
/** Numeric account ID, may be deprecated. */
ID
ID,
/** The user-settable status of this account (e.g. busy, OOO, available) */
STATUS
}
public abstract void fillAccountInfo(

View File

@ -42,6 +42,7 @@ public class AccountLoader {
FillOptions.NAME,
FillOptions.EMAIL,
FillOptions.USERNAME,
FillOptions.STATUS,
FillOptions.AVATARS));
public interface Factory {

View File

@ -0,0 +1,27 @@
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.account;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.inject.Singleton;
@Singleton
public class GetStatus implements RestReadView<AccountResource> {
@Override
public String apply(AccountResource rsrc) {
return Strings.nullToEmpty(rsrc.getUser().getAccount().getStatus());
}
}

View File

@ -103,6 +103,11 @@ public class InternalAccountDirectory extends AccountDirectory {
? AccountState.getUserName(externalIds)
: null;
}
if (options.contains(FillOptions.STATUS)) {
info.status = account.getStatus();
}
if (options.contains(FillOptions.AVATARS)) {
AvatarProvider ap = avatar.get();
if (ap != null) {

View File

@ -44,6 +44,8 @@ public class Module extends RestApiModule {
get(ACCOUNT_KIND, "name").to(GetName.class);
put(ACCOUNT_KIND, "name").to(PutName.class);
delete(ACCOUNT_KIND, "name").to(PutName.class);
get(ACCOUNT_KIND, "status").to(GetStatus.class);
put(ACCOUNT_KIND, "status").to(PutStatus.class);
get(ACCOUNT_KIND, "username").to(GetUsername.class);
put(ACCOUNT_KIND, "username").to(PutUsername.class);
get(ACCOUNT_KIND, "active").to(GetActive.class);

View File

@ -0,0 +1,91 @@
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.account;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PutStatus.Input;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collections;
@Singleton
public class PutStatus implements RestModifyView<AccountResource, Input> {
public static class Input {
@DefaultInput
String status;
public Input(String status) {
this.status = status;
}
public Input() {
}
}
private final Provider<CurrentUser> self;
private final Provider<ReviewDb> dbProvider;
private final AccountCache byIdCache;
@Inject
PutStatus(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider,
AccountCache byIdCache) {
this.self = self;
this.dbProvider = dbProvider;
this.byIdCache = byIdCache;
}
@Override
public Response<String> apply(AccountResource rsrc, Input input)
throws AuthException,
ResourceNotFoundException, OrmException, IOException {
if (self.get() != rsrc.getUser()
&& !self.get().getCapabilities().canModifyAccount()) {
throw new AuthException("not allowed to set status");
}
return apply(rsrc.getUser(), input);
}
public Response<String> apply(IdentifiedUser user, Input input)
throws ResourceNotFoundException, OrmException,
IOException {
if (input == null) {
input = new Input();
}
Account a = dbProvider.get().accounts().get(user.getAccountId());
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
a.setStatus(Strings.nullToEmpty(input.status));
dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
return Strings.isNullOrEmpty(a.getStatus())
? Response.none()
: Response.ok(a.getStatus());
}
}

View File

@ -59,6 +59,7 @@ import com.google.gerrit.server.account.Index;
import com.google.gerrit.server.account.PostWatchedProjects;
import com.google.gerrit.server.account.PutActive;
import com.google.gerrit.server.account.PutAgreement;
import com.google.gerrit.server.account.PutStatus;
import com.google.gerrit.server.account.SetDiffPreferences;
import com.google.gerrit.server.account.SetEditPreferences;
import com.google.gerrit.server.account.SetPreferences;
@ -115,6 +116,7 @@ public class AccountApiImpl implements AccountApi {
private final Index index;
private final GetExternalIds getExternalIds;
private final DeleteExternalIds deleteExternalIds;
private final PutStatus putStatus;
@Inject
AccountApiImpl(AccountLoader.Factory ailf,
@ -148,6 +150,7 @@ public class AccountApiImpl implements AccountApi {
Index index,
GetExternalIds getExternalIds,
DeleteExternalIds deleteExternalIds,
PutStatus putStatus,
@Assisted AccountResource account) {
this.account = account;
this.accountLoaderFactory = ailf;
@ -181,6 +184,7 @@ public class AccountApiImpl implements AccountApi {
this.index = index;
this.getExternalIds = getExternalIds;
this.deleteExternalIds = deleteExternalIds;
this.putStatus = putStatus;
}
@Override
@ -373,6 +377,16 @@ public class AccountApiImpl implements AccountApi {
}
}
@Override
public void setStatus(String status) throws RestApiException {
PutStatus.Input in = new PutStatus.Input(status);
try {
putStatus.apply(account, in);
} catch (OrmException | IOException e) {
throw new RestApiException("Cannot set status", e);
}
}
@Override
public List<SshKeyInfo> listSshKeys() throws RestApiException {
try {