With media client APIs migrated to retrofit (#2998)

* With media client APIs migrated to retrofit

* Add test cases and java docs

* Fix test

* Fix build

* Fix build and other minor issues

* Fix tests
This commit is contained in:
Vivek Maskara 2019-06-20 20:56:07 +05:30
parent 828b5a3cd1
commit 78141cb609
17 changed files with 252 additions and 92 deletions

View file

@ -31,8 +31,8 @@ dependencies {
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
implementation 'com.facebook.fresco:fresco:1.13.0' implementation 'com.facebook.fresco:fresco:1.13.0'
implementation 'com.drewnoakes:metadata-extractor:2.11.0' implementation 'com.drewnoakes:metadata-extractor:2.11.0'
implementation 'com.dmitrybrant:wikimedia-android-data-client:0.0.18'
implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'org.apache.commons:commons-lang3:3.8.1'
implementation 'com.dmitrybrant:wikimedia-android-data-client:0.0.23'
// UI // UI
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'

View file

@ -4,6 +4,9 @@ import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.wikipedia.dataclient.mwapi.MwQueryPage; import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.gallery.ExtMetadata; import org.wikipedia.gallery.ExtMetadata;
@ -20,8 +23,6 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.CommonsDateUtil; import fr.free.nrw.commons.utils.CommonsDateUtil;
import fr.free.nrw.commons.utils.MediaDataExtractorUtil; import fr.free.nrw.commons.utils.MediaDataExtractorUtil;
@ -156,9 +157,9 @@ public class Media implements Parcelable {
page.title(), page.title(),
"", "",
0, 0,
safeParseDate(metadata.dateTimeOriginal().value()), safeParseDate(metadata.dateTime()),
safeParseDate(metadata.dateTime().value()), safeParseDate(metadata.dateTime()),
StringUtil.fromHtml(metadata.artist().value()).toString() StringUtil.fromHtml(metadata.artist()).toString()
); );
if (!StringUtils.isBlank(imageInfo.getThumbUrl())) { if (!StringUtils.isBlank(imageInfo.getThumbUrl())) {
@ -170,17 +171,17 @@ public class Media implements Parcelable {
language = "default"; language = "default";
} }
media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription().value())); media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription()));
media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories().value())); media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.getCategories()));
String latitude = metadata.gpsLatitude().value(); String latitude = metadata.getGpsLatitude();
String longitude = metadata.gpsLongitude().value(); String longitude = metadata.getGpsLongitude();
if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) { if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) {
LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0); LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0);
media.setCoordinates(latLng); media.setCoordinates(latLng);
} }
media.setLicenseInformation(metadata.licenseShortName().value(), metadata.licenseUrl().value()); media.setLicenseInformation(metadata.licenseShortName(), metadata.licenseUrl());
return media; return media;
} }

View file

@ -1,9 +1,11 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import androidx.core.text.HtmlCompat;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import androidx.core.text.HtmlCompat; import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import io.reactivex.Single; import io.reactivex.Single;
@ -19,12 +21,15 @@ import timber.log.Timber;
public class MediaDataExtractor { public class MediaDataExtractor {
private final MediaWikiApi mediaWikiApi; private final MediaWikiApi mediaWikiApi;
private final OkHttpJsonApiClient okHttpJsonApiClient; private final OkHttpJsonApiClient okHttpJsonApiClient;
private final MediaClient mediaClient;
@Inject @Inject
public MediaDataExtractor(MediaWikiApi mwApi, public MediaDataExtractor(MediaWikiApi mwApi,
OkHttpJsonApiClient okHttpJsonApiClient) { OkHttpJsonApiClient okHttpJsonApiClient,
MediaClient mediaClient) {
this.okHttpJsonApiClient = okHttpJsonApiClient; this.okHttpJsonApiClient = okHttpJsonApiClient;
this.mediaWikiApi = mwApi; this.mediaWikiApi = mwApi;
this.mediaClient = mediaClient;
} }
/** /**
@ -35,7 +40,7 @@ public class MediaDataExtractor {
*/ */
public Single<Media> fetchMediaDetails(String filename) { public Single<Media> fetchMediaDetails(String filename) {
Single<Media> mediaSingle = getMediaFromFileName(filename); Single<Media> mediaSingle = getMediaFromFileName(filename);
Single<Boolean> pageExistsSingle = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); Single<Boolean> pageExistsSingle = mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + filename);
Single<String> discussionSingle = getDiscussion(filename); Single<String> discussionSingle = getDiscussion(filename);
return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, (media, deletionStatus, discussion) -> { return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, (media, deletionStatus, discussion) -> {
media.setDiscussion(discussion); media.setDiscussion(discussion);

View file

@ -31,7 +31,7 @@ public final class OkHttpConnectionFactory {
return new OkHttpClient.Builder() return new OkHttpClient.Builder()
.cookieJar(SharedPreferenceCookieManager.getInstance()) .cookieJar(SharedPreferenceCookieManager.getInstance())
.cache(NET_CACHE) .cache(NET_CACHE)
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.addInterceptor(new UnsuccessfulResponseInterceptor()) .addInterceptor(new UnsuccessfulResponseInterceptor())
.addInterceptor(new CommonHeaderRequestInterceptor()) .addInterceptor(new CommonHeaderRequestInterceptor())
.build(); .build();

View file

@ -19,6 +19,7 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.media.MediaInterface;
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
@ -39,6 +40,8 @@ public class NetworkingModule {
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
private static final String NAMED_COMMONS_WIKI_SITE = "commons-wikisite";
@Provides @Provides
@Singleton @Singleton
public OkHttpClient provideOkHttpClient(Context context, public OkHttpClient provideOkHttpClient(Context context,
@ -120,7 +123,13 @@ public class NetworkingModule {
@Provides @Provides
@Singleton @Singleton
public ReviewInterface provideReviewInterface(@Named("commons-wikisite") WikiSite commonsWikiSite) { public ReviewInterface provideReviewInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class); return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class);
} }
@Provides
@Singleton
public MediaInterface provideMediaInterface(@Named(NAMED_COMMONS_WIKI_SITE) WikiSite commonsWikiSite) {
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, MediaInterface.class);
}
} }

View file

@ -0,0 +1,46 @@
package fr.free.nrw.commons.media;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Single;
/**
* Media Client to handle custom calls to Commons MediaWiki APIs
*/
@Singleton
public class MediaClient {
private final MediaInterface mediaInterface;
@Inject
public MediaClient(MediaInterface mediaInterface) {
this.mediaInterface = mediaInterface;
}
/**
* Checks if a page exists on Commons
* The same method can be used to check for file or talk page
*
* @param title File:Test.jpg or Commons:Deletion_requests/File:Test1.jpeg
*/
public Single<Boolean> checkPageExistsUsingTitle(String title) {
return mediaInterface.checkPageExistsUsingTitle(title)
.map(mwQueryResponse -> mwQueryResponse
.query().firstPage().pageId() > 0)
.singleOrError();
}
/**
* Take the fileSha and returns whether a file with a matching SHA exists or not
*
* @param fileSha SHA of the file to be checked
*/
public Single<Boolean> checkFileExistsUsingSha(String fileSha) {
return mediaInterface.checkFileExistsUsingSha(fileSha)
.map(mwQueryResponse -> mwQueryResponse
.query().allImages().size() > 0)
.singleOrError();
}
}

View file

@ -0,0 +1,28 @@
package fr.free.nrw.commons.media;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;
/**
* Interface for interacting with Commons media related APIs
*/
public interface MediaInterface {
/**
* Checks if a page exists or not.
* @param title the title of the page to be checked
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2")
Observable<MwQueryResponse> checkPageExistsUsingTitle(@Query("titles") String title);
/**
* Check if file exists
* @param aisha1 the SHA of the media file to be checked
* @return
*/
@GET("w/api.php?action=query&format=json&formatversion=2&list=allimages")
Observable<MwQueryResponse> checkFileExistsUsingSha(@Query("aisha1") String aisha1);
}

View file

@ -4,6 +4,9 @@ import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -32,8 +35,6 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
@ -224,23 +225,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
return centralAuthToken; return centralAuthToken;
} }
@Override
public boolean fileExistsWithName(String fileName) throws IOException {
return api.action("query")
.param("prop", "imageinfo")
.param("titles", "File:" + fileName)
.get()
.getNodes("/api/query/pages/page/imageinfo").size() > 0;
}
@Override
public Single<Boolean> pageExists(String pageName) {
return Single.fromCallable(() -> Double.parseDouble(api.action("query")
.param("titles", pageName)
.get()
.getString("/api/query/pages/page/@_idx")) != -1);
}
@Override @Override
public boolean thank(String editToken, long revision) throws IOException { public boolean thank(String editToken, long revision) throws IOException {
CustomApiResult res = api.action("thank") CustomApiResult res = api.action("thank")
@ -749,16 +733,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
return gson.fromJson(queryContinueString, QueryContinue.class); return gson.fromJson(queryContinueString, QueryContinue.class);
} }
@Override
public boolean existingFile(String fileSha1) throws IOException {
return api.action("query")
.param("format", "xml")
.param("list", "allimages")
.param("aisha1", fileSha1)
.get()
.getNodes("/api/query/allimages/img").size() > 0;
}
@Override @Override
@NonNull @NonNull
public Single<UploadStash> uploadFile( public Single<UploadStash> uploadFile(

View file

@ -30,10 +30,6 @@ public interface MediaWikiApi {
String getCentralAuthToken() throws IOException; String getCentralAuthToken() throws IOException;
boolean fileExistsWithName(String fileName) throws IOException;
Single<Boolean> pageExists(String pageName);
List<String> getSubCategoryList(String categoryName); List<String> getSubCategoryList(String categoryName);
List<String> getParentCategoryList(String categoryName); List<String> getParentCategoryList(String categoryName);
@ -87,8 +83,6 @@ public interface MediaWikiApi {
@Nullable @Nullable
String revisionsByFilename(String filename) throws IOException; String revisionsByFilename(String filename) throws IOException;
boolean existingFile(String fileSha1) throws IOException;
@NonNull @NonNull
LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException; LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException;

View file

@ -14,6 +14,7 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import io.reactivex.Observable; import io.reactivex.Observable;
@ -26,14 +27,16 @@ public class ReviewHelper {
private final OkHttpJsonApiClient okHttpJsonApiClient; private final OkHttpJsonApiClient okHttpJsonApiClient;
private final MediaWikiApi mediaWikiApi; private final MediaWikiApi mediaWikiApi;
private final MediaClient mediaClient;
private final ReviewInterface reviewInterface; private final ReviewInterface reviewInterface;
@Inject @Inject
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient, public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient,
MediaWikiApi mediaWikiApi, MediaWikiApi mediaWikiApi,
ReviewInterface reviewInterface) { MediaClient mediaClient, ReviewInterface reviewInterface) {
this.okHttpJsonApiClient = okHttpJsonApiClient; this.okHttpJsonApiClient = okHttpJsonApiClient;
this.mediaWikiApi = mediaWikiApi; this.mediaWikiApi = mediaWikiApi;
this.mediaClient = mediaClient;
this.reviewInterface = reviewInterface; this.reviewInterface = reviewInterface;
} }
@ -86,7 +89,7 @@ public class ReviewHelper {
*/ */
private Single<Media> getRandomMediaFromRecentChange(RecentChange recentChange) { private Single<Media> getRandomMediaFromRecentChange(RecentChange recentChange) {
return Single.just(recentChange) return Single.just(recentChange)
.flatMap(change -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + change.getTitle())) .flatMap(change -> mediaClient.checkPageExistsUsingTitle("Commons:Deletion_requests/" + change.getTitle()))
.flatMap(isDeleted -> { .flatMap(isDeleted -> {
if (isDeleted) { if (isDeleted) {
return Single.just(new Media("")); return Single.just(new Media(""));

View file

@ -10,6 +10,7 @@ import java.io.IOException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.ImageUtils;
@ -34,18 +35,20 @@ public class ImageProcessingService {
private final MediaWikiApi mwApi; private final MediaWikiApi mwApi;
private final ReadFBMD readFBMD; private final ReadFBMD readFBMD;
private final EXIFReader EXIFReader; private final EXIFReader EXIFReader;
private final MediaClient mediaClient;
private final Context context; private final Context context;
@Inject @Inject
public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper, public ImageProcessingService(FileUtilsWrapper fileUtilsWrapper,
ImageUtilsWrapper imageUtilsWrapper, ImageUtilsWrapper imageUtilsWrapper,
MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader, MediaWikiApi mwApi, ReadFBMD readFBMD, EXIFReader EXIFReader,
Context context) { MediaClient mediaClient, Context context) {
this.fileUtilsWrapper = fileUtilsWrapper; this.fileUtilsWrapper = fileUtilsWrapper;
this.imageUtilsWrapper = imageUtilsWrapper; this.imageUtilsWrapper = imageUtilsWrapper;
this.mwApi = mwApi; this.mwApi = mwApi;
this.readFBMD = readFBMD; this.readFBMD = readFBMD;
this.EXIFReader = EXIFReader; this.EXIFReader = EXIFReader;
this.mediaClient = mediaClient;
this.context = context; this.context = context;
} }
@ -128,7 +131,7 @@ public class ImageProcessingService {
return Single.just(EMPTY_TITLE); return Single.just(EMPTY_TITLE);
} }
return Single.fromCallable(() -> mwApi.fileExistsWithName(uploadItem.getFileName())) return mediaClient.checkPageExistsUsingTitle("File:" + uploadItem.getFileName())
.map(doesFileExist -> { .map(doesFileExist -> {
Timber.d("Result for valid title is %s", doesFileExist); Timber.d("Result for valid title is %s", doesFileExist);
return doesFileExist ? FILE_NAME_EXISTS : IMAGE_OK; return doesFileExist ? FILE_NAME_EXISTS : IMAGE_OK;
@ -146,7 +149,7 @@ public class ImageProcessingService {
return Single.fromCallable(() -> return Single.fromCallable(() ->
fileUtilsWrapper.getFileInputStream(filePath)) fileUtilsWrapper.getFileInputStream(filePath))
.map(fileUtilsWrapper::getSHA1) .map(fileUtilsWrapper::getSHA1)
.map(mwApi::existingFile) .flatMap(mediaClient::checkFileExistsUsingSha)
.map(b -> { .map(b -> {
Timber.d("Result for duplicate image %s", b); Timber.d("Result for duplicate image %s", b);
return b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK; return b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK;

View file

@ -8,9 +8,10 @@ import android.content.Intent;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import android.widget.Toast;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -33,6 +34,7 @@ import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.contributions.ContributionsContentProvider; import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.wikidata.WikidataEditService; import fr.free.nrw.commons.wikidata.WikidataEditService;
import io.reactivex.Single; import io.reactivex.Single;
@ -54,6 +56,8 @@ public class UploadService extends HandlerService<Contribution> {
@Inject WikidataEditService wikidataEditService; @Inject WikidataEditService wikidataEditService;
@Inject SessionManager sessionManager; @Inject SessionManager sessionManager;
@Inject ContributionDao contributionDao; @Inject ContributionDao contributionDao;
@Inject
MediaClient mediaClient;
private NotificationManagerCompat notificationManager; private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder curNotification; private NotificationCompat.Builder curNotification;
@ -337,7 +341,7 @@ public class UploadService extends HandlerService<Contribution> {
sequenceFileName = regexMatcher.replaceAll("$1 " + sequenceNumber + "$2"); sequenceFileName = regexMatcher.replaceAll("$1 " + sequenceNumber + "$2");
} }
} }
if (!mwApi.fileExistsWithName(sequenceFileName) if (!mediaClient.checkPageExistsUsingTitle(sequenceFileName).blockingGet()
&& !unfinishedUploads.contains(sequenceFileName)) { && !unfinishedUploads.contains(sequenceFileName)) {
break; break;
} }

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons package fr.free.nrw.commons
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.mwapi.MediaResult import fr.free.nrw.commons.mwapi.MediaResult
import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.mwapi.MediaWikiApi
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
@ -25,6 +26,9 @@ class MediaDataExtractorTest {
@Mock @Mock
internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null
@Mock
internal var mediaClient: MediaClient? = null
@InjectMocks @InjectMocks
var mediaDataExtractor: MediaDataExtractor? = null var mediaDataExtractor: MediaDataExtractor? = null
@ -45,7 +49,7 @@ class MediaDataExtractorTest {
`when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())) `when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean()))
.thenReturn(Single.just(mock(Media::class.java))) .thenReturn(Single.just(mock(Media::class.java)))
`when`(mwApi?.pageExists(ArgumentMatchers.anyString())) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Single.just(true)) .thenReturn(Single.just(true))
val mediaResult = mock(MediaResult::class.java) val mediaResult = mock(MediaResult::class.java)

View file

@ -0,0 +1,100 @@
package fr.free.nrw.commons.media
import io.reactivex.Observable
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.wikipedia.dataclient.mwapi.ImageDetails
import org.wikipedia.dataclient.mwapi.MwQueryPage
import org.wikipedia.dataclient.mwapi.MwQueryResponse
import org.wikipedia.dataclient.mwapi.MwQueryResult
class MediaClientTest {
@Mock
internal var mediaInterface: MediaInterface? = null
@InjectMocks
var mediaClient: MediaClient? = null
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
}
@Test
fun checkPageExistsUsingTitle() {
val mwQueryPage = mock(MwQueryPage::class.java)
`when`(mwQueryPage.pageId()).thenReturn(10)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult.firstPage()).thenReturn(mwQueryPage)
`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
val mockResponse = mock(MwQueryResponse::class.java)
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertTrue(checkPageExistsUsingTitle)
}
@Test
fun checkPageNotExistsUsingTitle() {
val mwQueryPage = mock(MwQueryPage::class.java)
`when`(mwQueryPage.pageId()).thenReturn(0)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult.firstPage()).thenReturn(mwQueryPage)
`when`(mwQueryResult.pages()).thenReturn(listOf())
val mockResponse = mock(MwQueryResponse::class.java)
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
val checkPageExistsUsingTitle = mediaClient!!.checkPageExistsUsingTitle("File:Test.jpg").blockingGet()
assertFalse(checkPageExistsUsingTitle)
}
@Test
fun checkFileExistsUsingSha() {
val mwQueryPage = mock(MwQueryPage::class.java)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult.allImages()).thenReturn(listOf(mock(ImageDetails::class.java)))
`when`(mwQueryResult.firstPage()).thenReturn(mwQueryPage)
`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
val mockResponse = mock(MwQueryResponse::class.java)
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertTrue(checkFileExistsUsingSha)
}
@Test
fun checkFileNotExistsUsingSha() {
val mwQueryPage = mock(MwQueryPage::class.java)
val mwQueryResult = mock(MwQueryResult::class.java)
`when`(mwQueryResult.allImages()).thenReturn(listOf())
`when`(mwQueryResult.firstPage()).thenReturn(mwQueryPage)
`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
val mockResponse = mock(MwQueryResponse::class.java)
`when`(mockResponse.query()).thenReturn(mwQueryResult)
`when`(mediaInterface!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(Observable.just(mockResponse))
val checkFileExistsUsingSha = mediaClient!!.checkFileExistsUsingSha("abcde").blockingGet()
assertFalse(checkFileExistsUsingSha)
}
}

View file

@ -221,24 +221,6 @@ class ApacheHttpClientMediaWikiApiTest {
assertEquals("baz", result) assertEquals("baz", result)
} }
@Test
fun fileExistsWithName_FileNotFound() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api batchcomplete=\"\"><query> <normalized><n from=\"File:foo\" to=\"File:Foo\" /></normalized><pages><page _idx=\"-1\" ns=\"6\" title=\"File:Foo\" missing=\"\" imagerepository=\"\" /></pages></query></api>"))
val result = testObject.fileExistsWithName("foo")
assertBasicRequestParameters(server, "GET").let { request ->
parseQueryParams(request).let { params ->
assertEquals("xml", params["format"])
assertEquals("query", params["action"])
assertEquals("imageinfo", params["prop"])
assertEquals("File:foo", params["titles"])
}
}
assertFalse(result)
}
@Test @Test
fun isUserBlockedFromCommonsForInfinitelyBlockedUser() { fun isUserBlockedFromCommonsForInfinitelyBlockedUser() {
server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"infinite\"></userinfo></query></api>")) server.enqueue(MockResponse().setBody("<?xml version=\"1.0\"?><api><query><userinfo id=\"1000\" name=\"testusername\" blockid=\"3000\" blockedby=\"blockerusername\" blockedbyid=\"1001\" blockreason=\"testing\" blockedtimestamp=\"2018-05-24T15:32:09Z\" blockexpiry=\"infinite\"></userinfo></query></api>"))

View file

@ -1,6 +1,7 @@
package fr.free.nrw.commons.review package fr.free.nrw.commons.review
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.mwapi.MediaWikiApi
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import io.reactivex.Observable import io.reactivex.Observable
@ -31,6 +32,8 @@ class ReviewHelperTest {
internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null
@Mock @Mock
internal var mediaWikiApi: MediaWikiApi? = null internal var mediaWikiApi: MediaWikiApi? = null
@Mock
internal var mediaClient: MediaClient? = null
@InjectMocks @InjectMocks
var reviewHelper: ReviewHelper? = null var reviewHelper: ReviewHelper? = null
@ -74,7 +77,7 @@ class ReviewHelperTest {
*/ */
@Test @Test
fun getRandomMedia() { fun getRandomMedia() {
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString())) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Single.just(false)) .thenReturn(Single.just(false))
val randomMedia = reviewHelper?.randomMedia?.blockingGet() val randomMedia = reviewHelper?.randomMedia?.blockingGet()
@ -89,7 +92,7 @@ class ReviewHelperTest {
*/ */
@Test(expected = RuntimeException::class) @Test(expected = RuntimeException::class)
fun getRandomMediaWithWithAllMediaNominatedForDeletion() { fun getRandomMediaWithWithAllMediaNominatedForDeletion() {
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString())) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(Single.just(true)) .thenReturn(Single.just(true))
val media = reviewHelper?.randomMedia?.blockingGet() val media = reviewHelper?.randomMedia?.blockingGet()
assertNull(media) assertNull(media)
@ -101,11 +104,11 @@ class ReviewHelperTest {
*/ */
@Test @Test
fun getRandomMediaWithWithOneMediaNominatedForDeletion() { fun getRandomMediaWithWithOneMediaNominatedForDeletion() {
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test1.jpeg")) `when`(mediaClient?.checkPageExistsUsingTitle("Commons:Deletion_requests/File:Test1.jpeg"))
.thenReturn(Single.just(true)) .thenReturn(Single.just(true))
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test2.png")) `when`(mediaClient?.checkPageExistsUsingTitle("Commons:Deletion_requests/File:Test2.png"))
.thenReturn(Single.just(false)) .thenReturn(Single.just(false))
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test3.jpg")) `when`(mediaClient?.checkPageExistsUsingTitle("Commons:Deletion_requests/File:Test3.jpg"))
.thenReturn(Single.just(true)) .thenReturn(Single.just(true))
val media = reviewHelper?.randomMedia?.blockingGet() val media = reviewHelper?.randomMedia?.blockingGet()

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload
import android.net.Uri import android.net.Uri
import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.mwapi.MediaWikiApi
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.utils.ImageUtils import fr.free.nrw.commons.utils.ImageUtils
@ -28,6 +29,8 @@ class u {
internal var readFBMD: ReadFBMD?=null internal var readFBMD: ReadFBMD?=null
@Mock @Mock
internal var readEXIF: EXIFReader?=null internal var readEXIF: EXIFReader?=null
@Mock
internal var mediaClient: MediaClient? = null
@InjectMocks @InjectMocks
var imageProcessingService: ImageProcessingService? = null var imageProcessingService: ImageProcessingService? = null
@ -55,6 +58,7 @@ class u {
`when`(uploadItem.title).thenReturn(mockTitle) `when`(uploadItem.title).thenReturn(mockTitle)
`when`(uploadItem.place).thenReturn(mockPlace) `when`(uploadItem.place).thenReturn(mockPlace)
`when`(uploadItem.fileName).thenReturn("File:jpg")
`when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString())) `when`(fileUtilsWrapper!!.getFileInputStream(ArgumentMatchers.anyString()))
.thenReturn(mock(FileInputStream::class.java)) .thenReturn(mock(FileInputStream::class.java))
@ -74,10 +78,10 @@ class u {
.thenReturn(mock(FileInputStream::class.java)) .thenReturn(mock(FileInputStream::class.java))
`when`(fileUtilsWrapper!!.getSHA1(any(FileInputStream::class.java))) `when`(fileUtilsWrapper!!.getSHA1(any(FileInputStream::class.java)))
.thenReturn("fileSha") .thenReturn("fileSha")
`when`(mwApi!!.existingFile(ArgumentMatchers.anyString())) `when`(mediaClient!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(false) .thenReturn(Single.just(false))
`when`(mwApi!!.fileExistsWithName(ArgumentMatchers.anyString())) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(false) .thenReturn(Single.just(false))
`when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any())) `when`(readFBMD?.processMetadata(ArgumentMatchers.any(),ArgumentMatchers.any()))
.thenReturn(Single.just(ImageUtils.IMAGE_OK)) .thenReturn(Single.just(ImageUtils.IMAGE_OK))
`when`(readEXIF?.processMetadata(ArgumentMatchers.anyString())) `when`(readEXIF?.processMetadata(ArgumentMatchers.anyString()))
@ -93,8 +97,8 @@ class u {
@Test @Test
fun validateImageForDuplicateImage() { fun validateImageForDuplicateImage() {
`when`(mwApi!!.existingFile(ArgumentMatchers.anyString())) `when`(mediaClient!!.checkFileExistsUsingSha(ArgumentMatchers.anyString()))
.thenReturn(true) .thenReturn(Single.just(true))
val validateImage = imageProcessingService!!.validateImage(uploadItem, false) val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
assertEquals(ImageUtils.IMAGE_DUPLICATE, validateImage.blockingGet()) assertEquals(ImageUtils.IMAGE_DUPLICATE, validateImage.blockingGet())
} }
@ -123,16 +127,16 @@ class u {
@Test @Test
fun validateImageForFileNameExistsWithCheckTitleOff() { fun validateImageForFileNameExistsWithCheckTitleOff() {
`when`(mwApi!!.fileExistsWithName(ArgumentMatchers.anyString())) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(true) .thenReturn(Single.just(true))
val validateImage = imageProcessingService!!.validateImage(uploadItem, false) val validateImage = imageProcessingService!!.validateImage(uploadItem, false)
assertEquals(ImageUtils.IMAGE_OK, validateImage.blockingGet()) assertEquals(ImageUtils.IMAGE_OK, validateImage.blockingGet())
} }
@Test @Test
fun validateImageForFileNameExistsWithCheckTitleOn() { fun validateImageForFileNameExistsWithCheckTitleOn() {
`when`(mwApi!!.fileExistsWithName(ArgumentMatchers.nullable(String::class.java))) `when`(mediaClient?.checkPageExistsUsingTitle(ArgumentMatchers.anyString()))
.thenReturn(true) .thenReturn(Single.just(true))
val validateImage = imageProcessingService!!.validateImage(uploadItem, true) val validateImage = imageProcessingService!!.validateImage(uploadItem, true)
assertEquals(ImageUtils.FILE_NAME_EXISTS, validateImage.blockingGet()) assertEquals(ImageUtils.FILE_NAME_EXISTS, validateImage.blockingGet())
} }