mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Categories related client API's migrated to retrofit (#3053)
* Commit 1 * searchCategories migrated to retrofit * SearchCategoriesFragment migrated to new API * Removed unused code * Created tests * implemented searching by prefix fixed SearchCategoryFragment behaviour where the same categories would be added to the list instead of new ones. * added tests * Migrated searchTitles to searchCategories, function behaviour seems identical
This commit is contained in:
parent
78141cb609
commit
d5198be3e3
8 changed files with 264 additions and 170 deletions
|
|
@ -23,6 +23,7 @@ public class CategoriesModel{
|
|||
private static final int SEARCH_CATS_LIMIT = 25;
|
||||
|
||||
private final MediaWikiApi mwApi;
|
||||
private final CategoryClient categoryClient;
|
||||
private final CategoryDao categoryDao;
|
||||
private final JsonKvStore directKvStore;
|
||||
|
||||
|
|
@ -32,9 +33,11 @@ public class CategoriesModel{
|
|||
@Inject GpsCategoryModel gpsCategoryModel;
|
||||
@Inject
|
||||
public CategoriesModel(MediaWikiApi mwApi,
|
||||
CategoryClient categoryClient,
|
||||
CategoryDao categoryDao,
|
||||
@Named("default_preferences") JsonKvStore directKvStore) {
|
||||
this.mwApi = mwApi;
|
||||
this.categoryClient = categoryClient;
|
||||
this.categoryDao = categoryDao;
|
||||
this.directKvStore = directKvStore;
|
||||
this.categoriesCache = new HashMap<>();
|
||||
|
|
@ -121,8 +124,8 @@ public class CategoriesModel{
|
|||
}
|
||||
|
||||
//otherwise, search API for matching categories
|
||||
return mwApi
|
||||
.allCategories(term, SEARCH_CATS_LIMIT)
|
||||
return categoryClient
|
||||
.searchCategoriesForPrefix(term, SEARCH_CATS_LIMIT)
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +188,7 @@ public class CategoriesModel{
|
|||
* @return
|
||||
*/
|
||||
private Observable<CategoryItem> getTitleCategories(String title) {
|
||||
return mwApi.searchTitles(title, SEARCH_CATS_LIMIT)
|
||||
return categoryClient.searchCategories(title, SEARCH_CATS_LIMIT)
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Category Client to handle custom calls to Commons MediaWiki APIs
|
||||
*/
|
||||
@Singleton
|
||||
public class CategoryClient {
|
||||
|
||||
private final CategoryInterface CategoryInterface;
|
||||
|
||||
@Inject
|
||||
public CategoryClient(CategoryInterface CategoryInterface) {
|
||||
this.CategoryInterface = CategoryInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for categories containing the specified string.
|
||||
*
|
||||
* @param filter The string to be searched
|
||||
* @param itemLimit How many results are returned
|
||||
* @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result
|
||||
* @return
|
||||
*/
|
||||
public Observable<String> searchCategories(String filter, int itemLimit, int offset) {
|
||||
return responseToCategoryName(CategoryInterface.searchCategories(filter, itemLimit, offset));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for categories containing the specified string.
|
||||
*
|
||||
* @param filter The string to be searched
|
||||
* @param itemLimit How many results are returned
|
||||
* @return
|
||||
*/
|
||||
public Observable<String> searchCategories(String filter, int itemLimit) {
|
||||
return searchCategories(filter, itemLimit, 0);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for categories starting with the specified string.
|
||||
*
|
||||
* @param prefix The prefix to be searched
|
||||
* @param itemLimit How many results are returned
|
||||
* @param offset Starts returning items from the nth result. If offset is 9, the response starts with the 9th item of the search result
|
||||
* @return
|
||||
*/
|
||||
public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit, int offset) {
|
||||
return responseToCategoryName(CategoryInterface.searchCategoriesForPrefix(prefix, itemLimit, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for categories starting with the specified string.
|
||||
*
|
||||
* @param prefix The prefix to be searched
|
||||
* @param itemLimit How many results are returned
|
||||
* @return
|
||||
*/
|
||||
public Observable<String> searchCategoriesForPrefix(String prefix, int itemLimit) {
|
||||
return searchCategoriesForPrefix(prefix, itemLimit, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal function to reduce code reuse. Extracts the categories returned from MwQueryResponse.
|
||||
*
|
||||
* @param responseObservable The query response observable
|
||||
* @return Observable emitting the categories returned. If our search yielded "Category:Test", "Test" is emitted.
|
||||
*/
|
||||
private Observable<String> responseToCategoryName(Observable<MwQueryResponse> responseObservable) {
|
||||
return responseObservable
|
||||
.flatMap(mwQueryResponse -> {
|
||||
List<MwQueryPage> pages = mwQueryResponse.query().pages();
|
||||
if (pages != null)
|
||||
return Observable.fromIterable(pages);
|
||||
else
|
||||
Timber.d("No categories returned.");
|
||||
return Observable.empty();
|
||||
})
|
||||
.map(MwQueryPage::title)
|
||||
.doOnEach(s -> Timber.d("Category returned: %s", s))
|
||||
.map(cat -> cat.replace("Category:", ""));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* Interface for interacting with Commons category related APIs
|
||||
*/
|
||||
public interface CategoryInterface {
|
||||
|
||||
/**
|
||||
* Searches for categories with the specified name.
|
||||
* Replaces ApacheHttpClientMediaWikiApi#allCategories
|
||||
*
|
||||
* @param filter The string to be searched
|
||||
* @param itemLimit How many results are returned
|
||||
* @return
|
||||
*/
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=search&gsrnamespace=14")
|
||||
Observable<MwQueryResponse> searchCategories(@Query("gsrsearch") String filter,
|
||||
@Query("gsrlimit") int itemLimit, @Query("gsroffset") int offset);
|
||||
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2"
|
||||
+ "&generator=allcategories")
|
||||
Observable<MwQueryResponse> searchCategoriesForPrefix(@Query("gacprefix") String prefix,
|
||||
@Query("gaclimit") int itemLimit, @Query("gacoffset") int offset);
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
|
|||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.category.CategoryInterface;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.media.MediaInterface;
|
||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
|
|
@ -132,4 +133,10 @@ public class NetworkingModule {
|
|||
public MediaInterface provideMediaInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
|
||||
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, MediaInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CategoryInterface provideCategoryInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
|
||||
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, CategoryInterface.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import javax.inject.Named;
|
|||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.category.CategoryClient;
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearch;
|
||||
|
|
@ -58,9 +59,12 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
String query;
|
||||
@BindView(R.id.bottomProgressBar)
|
||||
ProgressBar bottomProgressBar;
|
||||
boolean isLoadingCategories;
|
||||
|
||||
@Inject RecentSearchesDao recentSearchesDao;
|
||||
@Inject MediaWikiApi mwApi;
|
||||
@Inject CategoryClient categoryClient;
|
||||
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
JsonKvStore basicKvStore;
|
||||
|
|
@ -135,33 +139,36 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
progressBar.setVisibility(GONE);
|
||||
queryList.clear();
|
||||
categoriesAdapter.clear();
|
||||
compositeDisposable.add(Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size()))
|
||||
compositeDisposable.add(categoryClient.searchCategories(query,25)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.doOnSubscribe(disposable -> saveQuery(query))
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds more results to existing search results
|
||||
* Adds 25 more results to existing search results
|
||||
*/
|
||||
public void addCategoriesToList(String query) {
|
||||
if(isLoadingCategories) return;
|
||||
isLoadingCategories=true;
|
||||
this.query = query;
|
||||
bottomProgressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.setVisibility(GONE);
|
||||
compositeDisposable.add(Observable.fromCallable(() -> mwApi.searchCategory(query,queryList.size()))
|
||||
compositeDisposable.add(categoryClient.searchCategories(query,25, queryList.size())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.collect(ArrayList<String>::new, ArrayList::add)
|
||||
.subscribe(this::handlePaginationSuccess, this::handleError));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* it initializes the recycler view by adding items to the adapter
|
||||
* @param mediaList
|
||||
*/
|
||||
private void handlePaginationSuccess(List<String> mediaList) {
|
||||
queryList.addAll(mediaList);
|
||||
|
|
@ -169,6 +176,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
bottomProgressBar.setVisibility(GONE);
|
||||
categoriesAdapter.addAll(mediaList);
|
||||
categoriesAdapter.notifyDataSetChanged();
|
||||
isLoadingCategories=false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -176,7 +184,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
/**
|
||||
* Handles the success scenario
|
||||
* it initializes the recycler view by adding items to the adapter
|
||||
* @param mediaList
|
||||
*/
|
||||
private void handleSuccess(List<String> mediaList) {
|
||||
queryList = mediaList;
|
||||
|
|
@ -194,7 +201,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
* @param throwable
|
||||
*/
|
||||
private void handleError(Throwable throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading queried categories");
|
||||
|
|
@ -213,7 +219,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
|
|||
private void initErrorView() {
|
||||
progressBar.setVisibility(GONE);
|
||||
categoriesNotFoundView.setVisibility(VISIBLE);
|
||||
categoriesNotFoundView.setText(getString(R.string.categories_not_found, query));
|
||||
categoriesNotFoundView.setText(getString(R.string.categories_not_found));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
status.equals("UI")
|
||||
&& loginCustomApiResult.getString("/api/clientlogin/requests/_v/@id").equals("TOTPAuthenticationRequest")
|
||||
&& loginCustomApiResult.getString("/api/clientlogin/requests/_v/@provider").equals("Two-factor authentication (OATH).")
|
||||
) {
|
||||
) {
|
||||
setAuthCookieOnLogin(false);
|
||||
return "2FA";
|
||||
}
|
||||
|
|
@ -251,7 +251,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
||||
|
|
@ -305,71 +304,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Observable<String> searchCategories(String filterValue, int searchCatsLimit) {
|
||||
List<String> categories = new ArrayList<>();
|
||||
return Single.fromCallable(() -> {
|
||||
List<CustomApiResult> categoryNodes = null;
|
||||
try {
|
||||
categoryNodes = api.action("query")
|
||||
.param("format", "xml")
|
||||
.param("list", "search")
|
||||
.param("srwhat", "text")
|
||||
.param("srnamespace", "14")
|
||||
.param("srlimit", searchCatsLimit)
|
||||
.param("srsearch", filterValue)
|
||||
.get()
|
||||
.getNodes("/api/query/search/p/@title");
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to obtain searchCategories");
|
||||
}
|
||||
|
||||
if (categoryNodes == null) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
for (CustomApiResult categoryNode : categoryNodes) {
|
||||
String cat = categoryNode.getDocument().getTextContent();
|
||||
String catString = cat.replace("Category:", "");
|
||||
if (!categories.contains(catString)) {
|
||||
categories.add(catString);
|
||||
}
|
||||
}
|
||||
|
||||
return categories;
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Observable<String> allCategories(String filterValue, int searchCatsLimit) {
|
||||
return Single.fromCallable(() -> {
|
||||
ArrayList<CustomApiResult> categoryNodes = null;
|
||||
try {
|
||||
categoryNodes = api.action("query")
|
||||
.param("list", "allcategories")
|
||||
.param("acprefix", filterValue)
|
||||
.param("aclimit", searchCatsLimit)
|
||||
.get()
|
||||
.getNodes("/api/query/allcategories/c");
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to obtain allCategories");
|
||||
}
|
||||
|
||||
if (categoryNodes == null) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
List<String> categories = new ArrayList<>();
|
||||
for (CustomApiResult categoryNode : categoryNodes) {
|
||||
categories.add(categoryNode.getDocument().getTextContent());
|
||||
}
|
||||
|
||||
return categories;
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWikidataCsrfToken() throws IOException {
|
||||
String wikidataCsrfToken = wikidataApi.action("query")
|
||||
|
|
@ -385,10 +319,11 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
/**
|
||||
* Creates a new claim using the wikidata API
|
||||
* https://www.mediawiki.org/wiki/Wikibase/API
|
||||
*
|
||||
* @param entityId the wikidata entity to be edited
|
||||
* @param property the property to be edited, for eg P18 for images
|
||||
* @param snaktype the type of value stored for that property
|
||||
* @param value the actual value to be stored for the property, for eg filename in case of P18
|
||||
* @param value the actual value to be stored for the property, for eg filename in case of P18
|
||||
* @return returns revisionId if the claim is successfully created else returns null
|
||||
* @throws IOException
|
||||
*/
|
||||
|
|
@ -422,6 +357,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
|
||||
/**
|
||||
* Adds the wikimedia-commons-app tag to the edits made on wikidata
|
||||
*
|
||||
* @param revisionId
|
||||
* @return
|
||||
* @throws IOException
|
||||
|
|
@ -451,42 +387,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Observable<String> searchTitles(String title, int searchCatsLimit) {
|
||||
return Single.fromCallable((Callable<List<String>>) () -> {
|
||||
ArrayList<CustomApiResult> categoryNodes;
|
||||
|
||||
try {
|
||||
categoryNodes = api.action("query")
|
||||
.param("format", "xml")
|
||||
.param("list", "search")
|
||||
.param("srwhat", "text")
|
||||
.param("srnamespace", "14")
|
||||
.param("srlimit", searchCatsLimit)
|
||||
.param("srsearch", title)
|
||||
.get()
|
||||
.getNodes("/api/query/search/p/@title");
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to obtain searchTitles");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (categoryNodes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> titleCategories = new ArrayList<>();
|
||||
for (CustomApiResult categoryNode : categoryNodes) {
|
||||
String cat = categoryNode.getDocument().getTextContent();
|
||||
String catString = cat.replace("Category:", "");
|
||||
titleCategories.add(catString);
|
||||
}
|
||||
|
||||
return titleCategories;
|
||||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException {
|
||||
|
|
@ -543,13 +443,13 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
try {
|
||||
if (archived) {
|
||||
notfilter = "read";
|
||||
}else {
|
||||
} else {
|
||||
notfilter = "!read";
|
||||
}
|
||||
String language=Locale.getDefault().getLanguage();
|
||||
if(StringUtils.isBlank(language)){
|
||||
String language = Locale.getDefault().getLanguage();
|
||||
if (StringUtils.isBlank(language)) {
|
||||
//if no language is set we use the default user language defined on wikipedia
|
||||
language="user";
|
||||
language = "user";
|
||||
}
|
||||
notificationNode = api.action("query")
|
||||
.param("notprop", "list")
|
||||
|
|
@ -595,6 +495,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
* The method takes categoryName as input and returns a List of Subcategories
|
||||
* It uses the generator query API to get the subcategories in a category, 500 at a time.
|
||||
* Uses the query continue values for fetching paginated responses
|
||||
*
|
||||
* @param categoryName Category name as defined on commons
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -606,7 +507,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
CustomMwApi.RequestBuilder requestBuilder = api.action("query")
|
||||
.param("generator", "categorymembers")
|
||||
.param("format", "xml")
|
||||
.param("gcmtype","subcat")
|
||||
.param("gcmtype", "subcat")
|
||||
.param("gcmtitle", categoryName)
|
||||
.param("prop", "info")
|
||||
.param("gcmlimit", "500")
|
||||
|
|
@ -636,6 +537,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
/**
|
||||
* The method takes categoryName as input and returns a List of parent categories
|
||||
* It uses the generator query API to get the parent categories of a category, 500 at a time.
|
||||
*
|
||||
* @param categoryName Category name as defined on commons
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -673,48 +575,12 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
return CategoryImageUtils.getSubCategoryList(childNodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes search keyword as input and returns a list of categories objects filtered using search query
|
||||
* It uses the generator query API to get the categories searched using a query, 25 at a time.
|
||||
* @param query keyword to search categories on commons
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public List<String> searchCategory(String query, int offset) {
|
||||
List<CustomApiResult> categoryNodes = null;
|
||||
try {
|
||||
categoryNodes = api.action("query")
|
||||
.param("format", "xml")
|
||||
.param("list", "search")
|
||||
.param("srwhat", "text")
|
||||
.param("srnamespace", "14")
|
||||
.param("srlimit", "25")
|
||||
.param("sroffset",offset)
|
||||
.param("srsearch", query)
|
||||
.get()
|
||||
.getNodes("/api/query/search/p/@title");
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Failed to obtain searchCategories");
|
||||
}
|
||||
|
||||
if (categoryNodes == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<String> categories = new ArrayList<>();
|
||||
for (CustomApiResult categoryNode : categoryNodes) {
|
||||
String catName = categoryNode.getDocument().getTextContent();
|
||||
categories.add(catName);
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages
|
||||
* https://www.mediawiki.org/wiki/API:Raw_query_continue
|
||||
* After fetching images a page of image for a particular category, shared defaultKvStore are updated with the latest QueryContinue Values
|
||||
*
|
||||
* @param keyword
|
||||
* @param queryContinue
|
||||
*/
|
||||
|
|
@ -724,6 +590,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
|
||||
/**
|
||||
* Before making a paginated API call, this method is called to get the latest query continue values to be used
|
||||
*
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -751,7 +618,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
if (!resultStatus.equals("Success")) {
|
||||
String errorCode = result.getString("/api/error/@code");
|
||||
Timber.e(errorCode);
|
||||
|
||||
|
||||
if (errorCode.equals(ERROR_CODE_BAD_TOKEN)) {
|
||||
ViewUtil.showLongToast(context, R.string.bad_token_error_proposed_solution);
|
||||
}
|
||||
|
|
@ -799,8 +666,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}
|
||||
|
||||
/**
|
||||
|
||||
* Checks to see if a user is currently blocked from Commons
|
||||
*
|
||||
* @return whether or not the user is blocked from Commons
|
||||
*/
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ public interface MediaWikiApi {
|
|||
|
||||
List<String> getParentCategoryList(String categoryName);
|
||||
|
||||
@NonNull
|
||||
List<String> searchCategory(String title, int offset);
|
||||
|
||||
@NonNull
|
||||
Single<UploadStash> uploadFile(String filename, InputStream file,
|
||||
long dataLength, Uri fileUri, Uri contentProviderUri,
|
||||
|
|
@ -65,21 +62,12 @@ public interface MediaWikiApi {
|
|||
@NonNull
|
||||
Single<MediaResult> fetchMediaByFilename(String filename);
|
||||
|
||||
@NonNull
|
||||
Observable<String> searchCategories(String filterValue, int searchCatsLimit);
|
||||
|
||||
@NonNull
|
||||
Observable<String> allCategories(String filter, int searchCatsLimit);
|
||||
|
||||
@NonNull
|
||||
List<Notification> getNotifications(boolean archived) throws IOException;
|
||||
|
||||
@NonNull
|
||||
boolean markNotificationAsRead(Notification notification) throws IOException;
|
||||
|
||||
@NonNull
|
||||
Observable<String> searchTitles(String title, int searchCatsLimit);
|
||||
|
||||
@Nullable
|
||||
String revisionsByFilename(String filename) throws IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
package fr.free.nrw.commons.category
|
||||
|
||||
import io.reactivex.Observable
|
||||
import junit.framework.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.*
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryPage
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse
|
||||
import org.wikipedia.dataclient.mwapi.MwQueryResult
|
||||
|
||||
class CategoryClientTest {
|
||||
@Mock
|
||||
internal var categoryInterface: CategoryInterface? = null
|
||||
|
||||
@InjectMocks
|
||||
var categoryClient: CategoryClient? = null
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchCategoriesFound() {
|
||||
val mwQueryPage = Mockito.mock(MwQueryPage::class.java)
|
||||
Mockito.`when`(mwQueryPage.title()).thenReturn("Category:Test")
|
||||
val mwQueryResult = Mockito.mock(MwQueryResult::class.java)
|
||||
Mockito.`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
|
||||
val mockResponse = Mockito.mock(MwQueryResponse::class.java)
|
||||
Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult)
|
||||
|
||||
Mockito.`when`(categoryInterface!!.searchCategories(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
|
||||
val actualCategoryName = categoryClient!!.searchCategories("tes", 10).blockingFirst()
|
||||
Assert.assertEquals("Test", actualCategoryName)
|
||||
|
||||
val actualCategoryName2 = categoryClient!!.searchCategories("tes", 10, 10).blockingFirst()
|
||||
Assert.assertEquals("Test", actualCategoryName2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchCategoriesNull() {
|
||||
val mwQueryResult = Mockito.mock(MwQueryResult::class.java)
|
||||
Mockito.`when`(mwQueryResult.pages()).thenReturn(null)
|
||||
val mockResponse = Mockito.mock(MwQueryResponse::class.java)
|
||||
Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult)
|
||||
|
||||
Mockito.`when`(categoryInterface!!.searchCategories(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
|
||||
categoryClient!!.searchCategories("tes", 10).subscribe(
|
||||
{ Assert.fail("SearchCategories returned element when it shouldn't have.") },
|
||||
{ s -> throw s })
|
||||
categoryClient!!.searchCategories("tes", 10, 10).subscribe(
|
||||
{ Assert.fail("SearchCategories returned element when it shouldn't have.") },
|
||||
{ s -> throw s })
|
||||
}
|
||||
@Test
|
||||
fun searchCategoriesForPrefixFound() {
|
||||
val mwQueryPage = Mockito.mock(MwQueryPage::class.java)
|
||||
Mockito.`when`(mwQueryPage.title()).thenReturn("Category:Test")
|
||||
val mwQueryResult = Mockito.mock(MwQueryResult::class.java)
|
||||
Mockito.`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
|
||||
val mockResponse = Mockito.mock(MwQueryResponse::class.java)
|
||||
Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult)
|
||||
|
||||
Mockito.`when`(categoryInterface!!.searchCategoriesForPrefix(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
|
||||
val actualCategoryName = categoryClient!!.searchCategoriesForPrefix("tes", 10).blockingFirst()
|
||||
Assert.assertEquals("Test", actualCategoryName)
|
||||
val actualCategoryName2 = categoryClient!!.searchCategoriesForPrefix("tes", 10, 10).blockingFirst()
|
||||
Assert.assertEquals("Test", actualCategoryName2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchCategoriesForPrefixNull() {
|
||||
val mwQueryResult = Mockito.mock(MwQueryResult::class.java)
|
||||
Mockito.`when`(mwQueryResult.pages()).thenReturn(null)
|
||||
val mockResponse = Mockito.mock(MwQueryResponse::class.java)
|
||||
Mockito.`when`(mockResponse.query()).thenReturn(mwQueryResult)
|
||||
|
||||
Mockito.`when`(categoryInterface!!.searchCategoriesForPrefix(ArgumentMatchers.anyString(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt()))
|
||||
.thenReturn(Observable.just(mockResponse))
|
||||
categoryClient!!.searchCategoriesForPrefix("tes", 10).subscribe(
|
||||
{ Assert.fail("SearchCategories returned element when it shouldn't have.") },
|
||||
{ s -> throw s })
|
||||
categoryClient!!.searchCategoriesForPrefix("tes", 10, 10).subscribe(
|
||||
{ Assert.fail("SearchCategories returned element when it shouldn't have.") },
|
||||
{ s -> throw s })
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue