Move login client out of the data-client (#5476)

This commit is contained in:
Paul Hawke 2024-01-26 08:11:44 -06:00 committed by GitHub
parent 8789879f10
commit 0541aacdff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 195 additions and 234 deletions

View file

@ -27,6 +27,7 @@ dependencies {
}
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation "com.squareup.retrofit2:converter-gson:2.8.1"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.8.1"
implementation 'com.squareup.okio:okio:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'

View file

@ -1,17 +1,14 @@
package fr.free.nrw.commons;
import androidx.annotation.NonNull;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import okhttp3.OkHttpClient;
import org.wikipedia.AppAdapter;
import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonMarshaller;
import org.wikipedia.json.GsonUnmarshaller;
import org.wikipedia.login.LoginResult;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import okhttp3.OkHttpClient;
public class CommonsAppAdapter extends AppAdapter {
private final int DEFAULT_THUMB_SIZE = 640;
@ -60,11 +57,6 @@ public class CommonsAppAdapter extends AppAdapter {
return sessionManager.getPassword();
}
@Override
public void updateAccount(@NonNull LoginResult result) {
sessionManager.updateAccount(result);
}
@Override
public SharedPreferenceCookieManager getCookies() {
if (!preferences.contains(COOKIE_STORE_NAME)) {

View file

@ -25,6 +25,9 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.NavUtils;
import androidx.core.content.ContextCompat;
import fr.free.nrw.commons.auth.login.LoginClient;
import fr.free.nrw.commons.auth.login.LoginInterface;
import fr.free.nrw.commons.auth.login.LoginResult;
import fr.free.nrw.commons.databinding.ActivityLoginBinding;
import fr.free.nrw.commons.utils.ActivityUtils;
import java.util.Locale;
@ -32,9 +35,7 @@ import org.wikipedia.AppAdapter;
import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.login.LoginClient;
import org.wikipedia.login.LoginClient.LoginCallback;
import org.wikipedia.login.LoginResult;
import fr.free.nrw.commons.auth.login.LoginClient.LoginCallback;
import javax.inject.Inject;
import javax.inject.Named;
@ -231,7 +232,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
private void doLogin(String username, String password, String twoFactorCode) {
progressDialog.show();
loginToken = ServiceFactory.get(commonsWikiSite).getLoginToken();
loginToken = ServiceFactory.get(commonsWikiSite, LoginInterface.class).getLoginToken();
loginToken.enqueue(
new Callback<MwQueryResponse>() {
@Override
@ -313,7 +314,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
compositeDisposable.clear();
sessionManager.setUserLoggedIn(true);
AppAdapter.get().updateAccount(loginResult);
sessionManager.updateAccount(loginResult);
progressDialog.dismiss();
showSuccessAndDismissDialog();
startMainActivity();

View file

@ -1,36 +0,0 @@
package fr.free.nrw.commons.auth;
import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.mwapi.MwPostResponse;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import io.reactivex.Observable;
/**
* Handler for logout
*/
@Singleton
public class LogoutClient {
private final Service service;
@Inject
public LogoutClient(@Named("commons-service") Service service) {
this.service = service;
}
/**
* Fetches the CSRF token and uses that to post the logout api call
* @return
*/
public Observable<MwPostResponse> postLogout() {
return service.getCsrfToken().concatMap(tokenResponse -> service.postLogout(
Objects.requireNonNull(Objects.requireNonNull(tokenResponse.query()).csrfToken())));
}
}

View file

@ -9,8 +9,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.wikipedia.login.LoginResult;
import fr.free.nrw.commons.auth.login.LoginResult;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

View file

@ -1,15 +1,16 @@
package fr.free.nrw.commons.auth.csrf
import androidx.annotation.VisibleForTesting
import fr.free.nrw.commons.auth.SessionManager
import org.wikipedia.AppAdapter
import org.wikipedia.dataclient.ServiceFactory
import org.wikipedia.dataclient.SharedPreferenceCookieManager
import org.wikipedia.dataclient.WikiSite
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.login.LoginClient
import org.wikipedia.login.LoginClient.LoginCallback
import org.wikipedia.login.LoginClient.LoginFailedException
import org.wikipedia.login.LoginResult
import fr.free.nrw.commons.auth.login.LoginClient
import fr.free.nrw.commons.auth.login.LoginClient.LoginCallback
import fr.free.nrw.commons.auth.login.LoginClient.LoginFailedException
import fr.free.nrw.commons.auth.login.LoginResult
import retrofit2.Call
import retrofit2.Response
import timber.log.Timber
@ -17,7 +18,10 @@ import java.io.IOException
import java.util.concurrent.Callable
import java.util.concurrent.Executors.newSingleThreadExecutor
class CsrfTokenClient(private val csrfWikiSite: WikiSite) {
class CsrfTokenClient(
private val csrfWikiSite: WikiSite,
private val sessionManager: SessionManager
) {
private var retries = 0
private var csrfTokenCall: Call<MwQueryResponse?>? = null
private val loginClient = LoginClient()
@ -33,7 +37,8 @@ class CsrfTokenClient(private val csrfWikiSite: WikiSite) {
try {
if (retry > 0) {
// Log in explicitly
LoginClient().loginBlocking(csrfWikiSite, userName, password, "")
LoginClient()
.loginBlocking(csrfWikiSite, userName, password, "")
}
// Get CSRFToken response off the main thread.
@ -121,10 +126,11 @@ class CsrfTokenClient(private val csrfWikiSite: WikiSite) {
password: String,
callback: Callback,
retryCallback: () -> Unit
) = LoginClient().request(csrfWikiSite, username, password, object : LoginCallback {
) = LoginClient()
.request(csrfWikiSite, username, password, object : LoginCallback {
override fun success(loginResult: LoginResult) {
if (loginResult.pass()) {
AppAdapter.get().updateAccount(loginResult)
sessionManager.updateAccount(loginResult)
retryCallback()
} else {
callback.failure(LoginFailedException(loginResult.message))

View file

@ -1,4 +1,4 @@
package org.wikipedia.login;
package fr.free.nrw.commons.auth.login;
import android.annotation.SuppressLint;
import android.text.TextUtils;
@ -52,7 +52,7 @@ public class LoginClient {
@NonNull final String password, @NonNull final LoginCallback cb) {
cancel();
tokenCall = ServiceFactory.get(wiki).getLoginToken();
tokenCall = ServiceFactory.get(wiki, LoginInterface.class).getLoginToken();
tokenCall.enqueue(new Callback<MwQueryResponse>() {
@Override public void onResponse(@NonNull Call<MwQueryResponse> call,
@NonNull Response<MwQueryResponse> response) {
@ -75,8 +75,8 @@ public class LoginClient {
@Nullable final String loginToken, @NonNull final String userLanguage, @NonNull final LoginCallback cb) {
this.userLanguage = userLanguage;
loginCall = TextUtils.isEmpty(twoFactorCode) && TextUtils.isEmpty(retypedPassword)
? ServiceFactory.get(wiki).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki).postLogIn(userName, password, retypedPassword, twoFactorCode, loginToken,
? ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, retypedPassword, twoFactorCode, loginToken,
userLanguage, true);
loginCall.enqueue(new Callback<LoginResponse>() {
@Override
@ -117,15 +117,15 @@ public class LoginClient {
public void loginBlocking(@NonNull final WikiSite wiki, @NonNull final String userName,
@NonNull final String password, @Nullable final String twoFactorCode) throws Throwable {
Response<MwQueryResponse> tokenResponse = ServiceFactory.get(wiki).getLoginToken().execute();
Response<MwQueryResponse> tokenResponse = ServiceFactory.get(wiki, LoginInterface.class).getLoginToken().execute();
if (tokenResponse.body() == null || TextUtils.isEmpty(tokenResponse.body().query().loginToken())) {
throw new IOException("Unexpected response when getting login token.");
}
String loginToken = tokenResponse.body().query().loginToken();
Call<LoginResponse> tempLoginCall = StringUtils.defaultIfEmpty(twoFactorCode, "").isEmpty()
? ServiceFactory.get(wiki).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki).postLogIn(userName, password, null, twoFactorCode, loginToken,
? ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, loginToken, userLanguage, Service.WIKIPEDIA_URL)
: ServiceFactory.get(wiki, LoginInterface.class).postLogIn(userName, password, null, twoFactorCode, loginToken,
userLanguage, true);
Response<LoginResponse> response = tempLoginCall.execute();
LoginResponse loginResponse = response.body();
@ -153,7 +153,7 @@ public class LoginClient {
@SuppressLint("CheckResult")
private void getExtendedInfo(@NonNull final WikiSite wiki, @NonNull String userName,
@NonNull final LoginResult loginResult, @NonNull final LoginCallback cb) {
ServiceFactory.get(wiki).getUserInfo(userName)
ServiceFactory.get(wiki, LoginInterface.class).getUserInfo(userName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {

View file

@ -0,0 +1,45 @@
package fr.free.nrw.commons.auth.login
import io.reactivex.Observable
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import retrofit2.Call
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.Query
interface LoginInterface {
@Headers("Cache-Control: no-cache")
@GET(Service.MW_API_PREFIX + "action=query&meta=tokens&type=login")
fun getLoginToken(): Call<MwQueryResponse?>
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(Service.MW_API_PREFIX + "action=clientlogin&rememberMe=")
fun postLogIn(
@Field("username") user: String?,
@Field("password") pass: String?,
@Field("logintoken") token: String?,
@Field("uselang") userLanguage: String?,
@Field("loginreturnurl") url: String?
): Call<LoginClient.LoginResponse?>
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(Service.MW_API_PREFIX + "action=clientlogin&rememberMe=")
fun postLogIn(
@Field("username") user: String?,
@Field("password") pass: String?,
@Field("retype") retypedPass: String?,
@Field("OATHToken") twoFactorCode: String?,
@Field("logintoken") token: String?,
@Field("uselang") userLanguage: String?,
@Field("logincontinue") loginContinue: Boolean
): Call<LoginClient.LoginResponse?>
@GET(Service.MW_API_PREFIX + "action=query&meta=userinfo&list=users&usprop=groups|cancreate")
fun getUserInfo(@Query("ususers") userName: String): Observable<MwQueryResponse?>
}

View file

@ -1,4 +1,4 @@
package org.wikipedia.login;
package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View file

@ -1,4 +1,4 @@
package org.wikipedia.login;
package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View file

@ -1,4 +1,4 @@
package org.wikipedia.login;
package fr.free.nrw.commons.auth.login;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View file

@ -10,6 +10,7 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.actions.PageEditInterface;
import fr.free.nrw.commons.actions.ThanksInterface;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.category.CategoryInterface;
import fr.free.nrw.commons.explore.depictions.DepictsClient;
import fr.free.nrw.commons.kvstore.JsonKvStore;
@ -40,7 +41,7 @@ import org.wikipedia.dataclient.Service;
import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.json.GsonUtil;
import org.wikipedia.login.LoginClient;
import fr.free.nrw.commons.auth.login.LoginClient;
import timber.log.Timber;
@Module
@ -105,8 +106,9 @@ public class NetworkingModule {
@Named(NAMED_COMMONS_CSRF)
@Provides
@Singleton
public CsrfTokenClient provideCommonsCsrfTokenClient(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
return new CsrfTokenClient(commonsWikiSite);
public CsrfTokenClient provideCommonsCsrfTokenClient(
@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite, SessionManager sessionManager) {
return new CsrfTokenClient(commonsWikiSite, sessionManager);
}
@Provides

View file

@ -11,7 +11,7 @@ import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.SharedPreferenceCookieManager
import org.wikipedia.json.GsonMarshaller
import org.wikipedia.login.LoginResult
import fr.free.nrw.commons.auth.login.LoginResult
class CommonsAppAdapterUnitTest {
@ -80,13 +80,6 @@ class CommonsAppAdapterUnitTest {
Assert.assertEquals(adapter.password, "test")
}
@Test
@Throws(Exception::class)
fun testUpdateAccount() {
adapter.updateAccount(result)
verify(sessionManager).updateAccount(result)
}
@Test
@Throws(Exception::class)
fun testSetCookies() {

View file

@ -8,7 +8,6 @@ import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.okhttp.TestStubInterceptor;
import org.wikipedia.dataclient.okhttp.UnsuccessfulResponseInterceptor;
import org.wikipedia.login.LoginResult;
public class TestAppAdapter extends AppAdapter {
@ -50,10 +49,6 @@ public class TestAppAdapter extends AppAdapter {
return null;
}
@Override
public void updateAccount(@NonNull LoginResult result) {
}
@Override
public SharedPreferenceCookieManager getCookies() {
return null;

View file

@ -8,13 +8,12 @@ import android.view.KeyEvent
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.TextView
import androidx.test.core.app.ApplicationProvider
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.auth.login.LoginResult
import fr.free.nrw.commons.kvstore.JsonKvStore
import org.junit.Assert
import org.junit.Before
@ -29,7 +28,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.fakes.RoboMenuItem
import org.wikipedia.AppAdapter
import org.wikipedia.login.LoginResult
import java.lang.reflect.Method

View file

@ -1,45 +0,0 @@
package fr.free.nrw.commons.auth
import com.nhaarman.mockitokotlin2.verify
import io.reactivex.Observable
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.Service
import org.wikipedia.dataclient.mwapi.MwPostResponse
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult
import javax.inject.Inject
import javax.inject.Named
class LogoutClientTest {
@Mock @field:[Inject Named("commons-service")]
lateinit var service: Service
@InjectMocks
var logoutClient: LogoutClient? = null
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.openMocks(this)
val mwQueryResponse = mock(MwQueryResponse::class.java)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult!!.csrfToken()).thenReturn("test_token")
`when`(mwQueryResponse.query()).thenReturn(mwQueryResult)
`when`(service!!.csrfToken)
.thenReturn(Observable.just(mwQueryResponse))
}
@Test
fun postLogout() {
`when`(service!!.postLogout(anyString())).thenReturn(Observable.just(mock(MwPostResponse::class.java)))
logoutClient!!.postLogout()
verify(service, times(1))!!.csrfToken
}
}

View file

@ -5,6 +5,7 @@ import android.accounts.AccountManager
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.auth.login.LoginResult
import fr.free.nrw.commons.kvstore.JsonKvStore
import org.junit.Assert
import org.junit.Before
@ -17,7 +18,6 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import org.wikipedia.login.LoginResult
import java.lang.reflect.Method

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.auth.csrf
import com.google.gson.stream.MalformedJsonException
import fr.free.nrw.commons.MockWebServerTest
import fr.free.nrw.commons.auth.SessionManager
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
@ -16,8 +17,9 @@ import org.wikipedia.dataclient.okhttp.HttpStatusException
class CsrfTokenClientTest : MockWebServerTest() {
private val wikiSite = WikiSite("test.wikipedia.org")
private val subject = CsrfTokenClient(wikiSite)
private val cb = mock(CsrfTokenClient.Callback::class.java)
private val sessionManager = mock(SessionManager::class.java)
private val subject = CsrfTokenClient(wikiSite, sessionManager)
@Test
@Throws(Throwable::class)

View file

@ -0,0 +1,84 @@
package fr.free.nrw.commons.login;
import android.net.Uri;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.MalformedJsonException;
import fr.free.nrw.commons.MockWebServerTest;
import fr.free.nrw.commons.auth.login.LoginInterface;
import io.reactivex.observers.TestObserver;
import org.junit.Before;
import org.junit.Test;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.json.NamespaceTypeAdapter;
import org.wikipedia.json.PostProcessingTypeAdapter;
import org.wikipedia.json.UriTypeAdapter;
import org.wikipedia.json.WikiSiteTypeAdapter;
import org.wikipedia.page.Namespace;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class UserExtendedInfoClientTest extends MockWebServerTest {
private LoginInterface apiService;
@Override
@Before
public void setUp() throws Throwable {
super.setUp();
apiService = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(getGson()))
.baseUrl(server().getUrl())
.build()
.create(LoginInterface.class);
}
@Test
public void testRequestSuccess() throws Throwable {
enqueueFromFile("user_extended_info.json");
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer
.assertComplete()
.assertNoErrors()
.assertValue(
result -> result.query().userInfo().id() == 24531888
&& result.query().getUserResponse("USER").name().equals("USER")
);
}
@Test
public void testRequestResponse404() {
enqueue404();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer.assertError(Exception.class);
}
@Test
public void testRequestResponseMalformed() {
enqueueMalformed();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
apiService.getUserInfo("USER").subscribe(observer);
observer.assertError(MalformedJsonException.class);
}
private Gson getGson() {
return new GsonBuilder()
.registerTypeHierarchyAdapter(Uri.class, new UriTypeAdapter().nullSafe())
.registerTypeHierarchyAdapter(Namespace.class, new NamespaceTypeAdapter().nullSafe())
.registerTypeAdapter(WikiSite.class, new WikiSiteTypeAdapter().nullSafe())
.registerTypeAdapterFactory(new PostProcessingTypeAdapter())
.create();
}
}

View file

@ -0,0 +1,19 @@
{
"batchcomplete": true,
"query": {
"users": [
{
"userid": 24531888,
"name": "USER",
"implicitgroups": [
"*",
"user"
]
}
],
"userinfo": {
"id": 24531888,
"name": "USER"
}
}
}

View file

@ -4,7 +4,6 @@ import androidx.annotation.NonNull;
import org.wikipedia.dataclient.SharedPreferenceCookieManager;
import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.login.LoginResult;
import okhttp3.OkHttpClient;
@ -18,7 +17,6 @@ public abstract class AppAdapter {
public abstract boolean isLoggedIn();
public abstract String getUserName();
public abstract String getPassword();
public abstract void updateAccount(@NonNull LoginResult result);
public abstract SharedPreferenceCookieManager getCookies();
public abstract void setCookies(@NonNull SharedPreferenceCookieManager cookies);

View file

@ -13,7 +13,6 @@ import org.wikipedia.dataclient.mwapi.page.MwMobileViewPageRemaining;
import org.wikipedia.dataclient.mwapi.page.MwQueryPageSummary;
import org.wikipedia.edit.Edit;
import org.wikipedia.edit.preview.EditPreview;
import org.wikipedia.login.LoginClient;
import org.wikipedia.search.PrefixSearchResponse;
import org.wikipedia.wikidata.Entities;
@ -190,54 +189,6 @@ public interface Service {
@NonNull Observable<MwQueryResponse> getCategoryMembers(@NonNull @Query("cmtitle") String title,
@Nullable @Query("cmcontinue") String continueStr);
// ------- CSRF, Login, and Create Account -------
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=tokens&type=csrf")
@NonNull Observable<MwQueryResponse> getCsrfToken();
@SuppressWarnings("checkstyle:parameternumber")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=createaccount&createmessageformat=html")
@NonNull Observable<CreateAccountResponse> postCreateAccount(@NonNull @Field("username") String user,
@NonNull @Field("password") String pass,
@NonNull @Field("retype") String retype,
@NonNull @Field("createtoken") String token,
@NonNull @Field("createreturnurl") String returnurl,
@Nullable @Field("email") String email,
@Nullable @Field("captchaId") String captchaId,
@Nullable @Field("captchaWord") String captchaWord);
@Headers("Cache-Control: no-cache")
@GET(MW_API_PREFIX + "action=query&meta=tokens&type=login")
@NonNull Call<MwQueryResponse> getLoginToken();
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=clientlogin&rememberMe=")
@NonNull Call<LoginClient.LoginResponse> postLogIn(@Field("username") String user, @Field("password") String pass,
@Field("logintoken") String token, @Field("uselang") String userLanguage, @Field("loginreturnurl") String url);
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=clientlogin&rememberMe=")
@NonNull Call<LoginClient.LoginResponse> postLogIn(@Field("username") String user, @Field("password") String pass,
@Field("retype") String retypedPass, @Field("OATHToken") String twoFactorCode,
@Field("logintoken") String token, @Field("uselang") String userLanguage,
@Field("logincontinue") boolean loginContinue);
@Headers("Cache-Control: no-cache")
@FormUrlEncoded
@POST(MW_API_PREFIX + "action=logout")
@NonNull Observable<MwPostResponse> postLogout(@NonNull @Field("token") String token);
@GET(MW_API_PREFIX + "action=query&meta=authmanagerinfo|tokens&amirequestsfor=create&type=createaccount")
@NonNull Observable<MwQueryResponse> getAuthManagerInfo();
@GET(MW_API_PREFIX + "action=query&meta=userinfo&list=users&usprop=groups|cancreate")
@NonNull Observable<MwQueryResponse> getUserInfo(@Query("ususers") @NonNull String userName);
// ------- Notifications -------
@Headers("Cache-Control: no-cache")

View file

@ -1,44 +0,0 @@
package org.wikipedia.login;
import com.google.gson.stream.MalformedJsonException;
import org.junit.Test;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import org.wikipedia.test.MockRetrofitTest;
import io.reactivex.Observable;
import io.reactivex.observers.TestObserver;
public class UserExtendedInfoClientTest extends MockRetrofitTest {
@Test public void testRequestSuccess() throws Throwable {
enqueueFromFile("user_extended_info.json");
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
final int id = 24531888;
observer.assertComplete().assertNoErrors()
.assertValue(result -> result.query().userInfo().id() == id
&& result.query().getUserResponse("USER").name().equals("USER"));
}
@Test public void testRequestResponse404() {
enqueue404();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
observer.assertError(Exception.class);
}
@Test public void testRequestResponseMalformed() {
enqueueMalformed();
TestObserver<MwQueryResponse> observer = new TestObserver<>();
getObservable().subscribe(observer);
observer.assertError(MalformedJsonException.class);
}
private Observable<MwQueryResponse> getObservable() {
return getApiService().getUserInfo("USER");
}
}