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

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

View file

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

View file

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

View file

@ -19,6 +19,7 @@ import dagger.Module;
import dagger.Provides;
import fr.free.nrw.commons.BuildConfig;
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.MediaWikiApi;
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;
private static final String NAMED_COMMONS_WIKI_SITE = "commons-wikisite";
@Provides
@Singleton
public OkHttpClient provideOkHttpClient(Context context,
@ -120,7 +123,13 @@ public class NetworkingModule {
@Provides
@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);
}
@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.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
@ -32,8 +35,6 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
@ -224,23 +225,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
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
public boolean thank(String editToken, long revision) throws IOException {
CustomApiResult res = api.action("thank")
@ -749,16 +733,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
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
@NonNull
public Single<UploadStash> uploadFile(

View file

@ -30,10 +30,6 @@ public interface MediaWikiApi {
String getCentralAuthToken() throws IOException;
boolean fileExistsWithName(String fileName) throws IOException;
Single<Boolean> pageExists(String pageName);
List<String> getSubCategoryList(String categoryName);
List<String> getParentCategoryList(String categoryName);
@ -87,8 +83,6 @@ public interface MediaWikiApi {
@Nullable
String revisionsByFilename(String filename) throws IOException;
boolean existingFile(String fileSha1) throws IOException;
@NonNull
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 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.OkHttpJsonApiClient;
import io.reactivex.Observable;
@ -26,14 +27,16 @@ public class ReviewHelper {
private final OkHttpJsonApiClient okHttpJsonApiClient;
private final MediaWikiApi mediaWikiApi;
private final MediaClient mediaClient;
private final ReviewInterface reviewInterface;
@Inject
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient,
MediaWikiApi mediaWikiApi,
ReviewInterface reviewInterface) {
MediaClient mediaClient, ReviewInterface reviewInterface) {
this.okHttpJsonApiClient = okHttpJsonApiClient;
this.mediaWikiApi = mediaWikiApi;
this.mediaClient = mediaClient;
this.reviewInterface = reviewInterface;
}
@ -86,7 +89,7 @@ public class ReviewHelper {
*/
private Single<Media> getRandomMediaFromRecentChange(RecentChange 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 -> {
if (isDeleted) {
return Single.just(new Media(""));

View file

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

View file

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