package org.wikipedia.login;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.util.ArraySet;
import com.google.gson.annotations.SerializedName;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.dataclient.retrofit.MwCachedService;
import org.wikipedia.dataclient.retrofit.WikiCachedService;
import org.wikipedia.useroption.dataclient.UserInfo;
import org.wikipedia.util.log.L;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.http.POST;
import retrofit2.http.Query;
/**
* Retrofit DataClient to retrieve implicit user info and group membership information for a specific user.
*/
class UserExtendedInfoClient {
@NonNull private final WikiCachedService<Service> cachedService = new MwCachedService<>(Service.class);
@Nullable private Call<MwQueryResponse<QueryResult>> groupCall;
interface Callback {
void success(@NonNull Call<MwQueryResponse<QueryResult>> call, int id, @NonNull Set<String> groups);
void failure(@NonNull Call<MwQueryResponse<QueryResult>> call, @NonNull Throwable caught);
}
public Call<MwQueryResponse<QueryResult>> request(@NonNull WikiSite wiki, @NonNull String userName,
@NonNull Callback cb) {
return request(cachedService.service(wiki), userName, cb);
}
@VisibleForTesting Call<MwQueryResponse<QueryResult>> request(@NonNull Service service,
@NonNull final String userName,
@NonNull final Callback cb) {
cancel();
groupCall = service.request(userName);
groupCall.enqueue(new retrofit2.Callback<MwQueryResponse<QueryResult>>() {
@Override
public void onResponse(Call<MwQueryResponse<QueryResult>> call,
Response<MwQueryResponse<QueryResult>> response) {
final MwQueryResponse<QueryResult> body = response.body();
final QueryResult query = body.query();
if (response.body().success()) {
// noinspection ConstantConditions
int userId = query.id();
cb.success(call, userId, query.getGroupsFor(userName));
L.v("Found user ID: " + userId);
} else if (response.body().hasError()) {
// noinspection ConstantConditions
cb.failure(call, new LoginClient.LoginFailedException(
"Failed to retrieve user ID and group membership data. "
+ body.getError().toString()));
} else {
cb.failure(call, new LoginClient.LoginFailedException(
"Unexpected error trying to retrieve user ID and group membership data. "
+ body.toString()));
}
}
@Override
public void onFailure(Call<MwQueryResponse<QueryResult>> call, Throwable caught) {
cb.failure(call, caught);
}
});
return groupCall;
}
public void cancel() {
cancelTokenRequest();
}
private void cancelTokenRequest() {
if (groupCall == null) {
return;
}
groupCall.cancel();
groupCall = null;
}
static final class QueryResult {
@SuppressWarnings("MismatchedReadAndWriteOfArray") @SerializedName("users") @NonNull
private List<ListUsersResponse> users = Collections.emptyList();
@SuppressWarnings("unused") @SerializedName("userinfo") private UserInfo userInfo;
int id() {
return userInfo.id();
}
@NonNull Set<String> getGroupsFor(@NonNull String userName) {
if (!users.isEmpty()) {
for (ListUsersResponse user : users) {
final Set<String> groups = user.getGroupsFor(userName);
if (groups != null) {
return groups;
}
}
}
return Collections.emptySet();
}
private static final class ListUsersResponse {
@SerializedName("name") @Nullable private String name;
@SerializedName("implicitgroups") @Nullable private String[] implicitGroups;
@Nullable Set<String> getGroupsFor(@NonNull String userName) {
if (userName.equals(name) && implicitGroups != null) {
Set<String> groups = new ArraySet<>();
groups.addAll(Arrays.asList(implicitGroups));
return Collections.unmodifiableSet(groups);
} else {
return null;
}
}
}
}
@VisibleForTesting interface Service {
/** Request the implicit groups a user belongs to. */
@NonNull
@POST("w/api.php?action=query&format=json&formatversion=2&meta=userinfo&list=users&usprop=implicitgroups")
Call<MwQueryResponse<QueryResult>> request(@Query("ususers") @NonNull String userName);
}
}