Convert wikidata/mwapi to kotlin (part 4) (#6010)

* Convert ImageDetails to kotlin

* Convert MwException/MwServiceError to kotlin

* Convert ListUserResponse to kotlin

* Convert MwPostResponse to kotlin

* Convert MwQueryResponse to kotlin

* Convert MwQueryResult to kotlin

* Convert MwQueryPage to kotlin

---------

Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
Paul Hawke 2024-12-09 21:18:42 -06:00 committed by GitHub
parent 56ada36b83
commit 73311970c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 474 additions and 615 deletions

View file

@ -237,7 +237,7 @@ class LoginClient(
.subscribe({ response: MwQueryResponse? ->
loginResult.userId = response?.query()?.userInfo()?.id() ?: 0
loginResult.groups =
response?.query()?.getUserResponse(userName)?.groups ?: emptySet()
response?.query()?.getUserResponse(userName)?.getGroups() ?: emptySet()
cb.success(loginResult)
}, { caught: Throwable ->
Timber.e(caught, "Login succeeded but getting group information failed. ")

View file

@ -938,7 +938,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
Observable.defer {
thanksClient.thank(
firstRevision.revisionId
firstRevision.revisionId()
)
}
.subscribeOn(Schedulers.io())

View file

@ -201,7 +201,7 @@ class ReviewActivity : BaseActivity() {
val caption = getString(
R.string.review_is_uploaded_by,
fileName,
revision.user
revision.user()
)
binding.tvImageCaption.text = caption
binding.pbReviewImage.visibility = View.GONE

View file

@ -12,7 +12,6 @@ import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage
import java.util.ArrayList
import java.util.concurrent.Callable
import javax.inject.Inject
import javax.inject.Named
@ -27,7 +26,6 @@ import fr.free.nrw.commons.delete.DeleteHelper
import fr.free.nrw.commons.di.ApplicationlessInjection
import fr.free.nrw.commons.utils.ViewUtil
import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
@ -175,7 +173,7 @@ class ReviewController @Inject constructor(
if (firstRevision == null) return
Observable.defer {
thanksClient.thank(firstRevision!!.revisionId)
thanksClient.thank(firstRevision!!.revisionId())
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View file

@ -34,7 +34,7 @@ class ReviewHelper
reviewInterface
.getRecentChanges()
.map { it.query()?.pages() }
.map(MutableList<MwQueryPage>::shuffled)
.map { it.shuffled() }
.flatMapIterable { changes: List<MwQueryPage>? -> changes }
.filter { isChangeReviewable(it) }

View file

@ -3,18 +3,15 @@ package fr.free.nrw.commons.review
import android.graphics.Color
import android.os.Bundle
import android.text.Html
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
import fr.free.nrw.commons.databinding.FragmentReviewImageBinding
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
import java.util.ArrayList
import javax.inject.Inject
@ -126,7 +123,7 @@ class ReviewImageFragment : CommonsDaggerSupportFragment() {
enableButtons()
question = getString(R.string.review_thanks)
user = reviewActivity.reviewController.firstRevision?.user
user = reviewActivity.reviewController.firstRevision?.user()
?: savedInstanceState?.getString(SAVED_USER)
//if the user is null because of whatsoever reason, review will not be sent anyways

View file

@ -270,7 +270,7 @@ class UploadClient
if (uploadResult.upload == null) {
val exception = gson.fromJson(uploadResponse, MwException::class.java)
Timber.e(exception, "Error in uploading file from stash")
throw Exception(exception.getErrorCode())
throw Exception(exception.errorCode)
}
uploadResult.upload
}

View file

@ -1,17 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
public class ImageDetails {
private String name;
private String title;
@NonNull public String getName() {
return name;
}
@NonNull public String getTitle() {
return title;
}
}

View file

@ -0,0 +1,6 @@
package fr.free.nrw.commons.wikidata.mwapi
class ImageDetails {
val name: String? = null
val title: String? = null
}

View file

@ -1,26 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.ArraySet;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class ListUserResponse {
@SerializedName("name") @Nullable private String name;
private long userid;
@Nullable private List<String> groups;
@Nullable public String name() {
return name;
}
@NonNull public Set<String> getGroups() {
return groups != null ? new ArraySet<>(groups) : Collections.emptySet();
}
}

View file

@ -0,0 +1,12 @@
package fr.free.nrw.commons.wikidata.mwapi
class ListUserResponse {
private val name: String? = null
private val userid: Long = 0
private val groups: List<String>? = null
fun name(): String? = name
fun getGroups(): Set<String> =
groups?.toSet() ?: emptySet()
}

View file

@ -1,44 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.Nullable;
import java.util.List;
public class MwException extends RuntimeException {
@Nullable private final MwServiceError error;
@Nullable private final List<MwServiceError> errors;
public MwException(@Nullable MwServiceError error,
@Nullable final List<MwServiceError> errors) {
this.error = error;
this.errors = errors;
}
public String getErrorCode() {
if(error!=null) {
return error.getCode();
}
return errors != null ? errors.get(0).getCode() : null;
}
@Nullable public MwServiceError getError() {
return error;
}
@Nullable
public String getTitle() {
if (error != null) {
return error.getTitle();
}
return errors != null ? errors.get(0).getTitle() : null;
}
@Override
@Nullable
public String getMessage() {
if (error != null) {
return error.getDetails();
}
return errors != null ? errors.get(0).getDetails() : null;
}
}

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.wikidata.mwapi
class MwException(
val error: MwServiceError?,
private val errors: List<MwServiceError>?
) : RuntimeException() {
val errorCode: String?
get() = error?.code ?: errors?.get(0)?.code
val title: String?
get() = error?.title ?: errors?.get(0)?.title
override val message: String?
get() = error?.details ?: errors?.get(0)?.details
}

View file

@ -1,16 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.Nullable;
public class MwPostResponse extends MwResponse {
private int success;
public boolean success(@Nullable String result) {
return "success".equals(result);
}
public int getSuccessVal() {
return success;
}
}

View file

@ -0,0 +1,9 @@
package fr.free.nrw.commons.wikidata.mwapi
open class MwPostResponse : MwResponse() {
val successVal: Int = 0
fun success(result: String?): Boolean =
"success" == result
}

View file

@ -1,229 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
import org.apache.commons.lang3.StringUtils;
import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo;
import fr.free.nrw.commons.wikidata.model.BaseModel;
import java.util.Collections;
import java.util.List;
/**
* A class representing a standard page object as returned by the MediaWiki API.
*/
public class MwQueryPage extends BaseModel {
private int pageid;
private int index;
@NonNull private String title;
@NonNull private CategoryInfo categoryinfo;
@Nullable private List<Revision> revisions;
@SerializedName("fileusage") @Nullable private List<FileUsage> fileUsages;
@SerializedName("globalusage") @Nullable private List<GlobalUsage> globalUsages;
@Nullable private List<Coordinates> coordinates;
@Nullable private List<Category> categories;
@Nullable private Thumbnail thumbnail;
@Nullable private String description;
@SerializedName("imageinfo") @Nullable private List<ImageInfo> imageInfo;
@Nullable private String redirectFrom;
@Nullable private String convertedFrom;
@Nullable private String convertedTo;
@NonNull public String title() {
return title;
}
@NonNull public CategoryInfo categoryInfo() {
return categoryinfo;
}
public int index() {
return index;
}
@Nullable public List<Revision> revisions() {
return revisions;
}
@Nullable public List<Category> categories() {
return categories;
}
@Nullable public List<Coordinates> coordinates() {
// TODO: Handle null values in lists during deserialization, perhaps with a new
// @RequiredElements annotation and corresponding TypeAdapter
if (coordinates != null) {
coordinates.removeAll(Collections.singleton(null));
}
return coordinates;
}
public int pageId() {
return pageid;
}
@Nullable public String thumbUrl() {
return thumbnail != null ? thumbnail.source() : null;
}
@Nullable public String description() {
return description;
}
@Nullable public ImageInfo imageInfo() {
return imageInfo != null ? imageInfo.get(0) : null;
}
public void redirectFrom(@Nullable String from) {
redirectFrom = from;
}
public void convertedFrom(@Nullable String from) {
convertedFrom = from;
}
public void convertedTo(@Nullable String to) {
convertedTo = to;
}
public void appendTitleFragment(@Nullable String fragment) {
title += "#" + fragment;
}
public boolean checkWhetherFileIsUsedInWikis() {
if (globalUsages != null && globalUsages.size() > 0) {
return true;
}
if (fileUsages == null || fileUsages.size() == 0) {
return false;
}
final int totalCount = fileUsages.size();
/* Ignore usage under https://commons.wikimedia.org/wiki/User:Didym/Mobile_upload/
which has been a gallery of all of our uploads since 2014 */
for (final FileUsage fileUsage : fileUsages) {
if ( ! fileUsage.title().contains("User:Didym/Mobile upload")) {
return true;
}
}
return false;
}
public static class Revision {
@SerializedName("revid") private long revisionId;
private String user;
@SerializedName("contentformat") @NonNull private String contentFormat;
@SerializedName("contentmodel") @NonNull private String contentModel;
@SerializedName("timestamp") @NonNull private String timeStamp;
@NonNull private String content;
@NonNull public String content() {
return content;
}
@NonNull public String timeStamp() {
return StringUtils.defaultString(timeStamp);
}
public long getRevisionId() {
return revisionId;
}
@NonNull
public String getUser() {
return StringUtils.defaultString(user);
}
}
public static class Coordinates {
@Nullable private Double lat;
@Nullable private Double lon;
@Nullable public Double lat() {
return lat;
}
@Nullable public Double lon() {
return lon;
}
}
public static class CategoryInfo {
private boolean hidden;
private int size;
private int pages;
private int files;
private int subcats;
public boolean isHidden() {
return hidden;
}
}
static class Thumbnail {
private String source;
private int width;
private int height;
String source() {
return source;
}
}
public static class GlobalUsage {
@SerializedName("title") private String title;
@SerializedName("wiki")private String wiki;
@SerializedName("url") private String url;
public String getTitle() {
return title;
}
public String getWiki() {
return wiki;
}
public String getUrl() {
return url;
}
}
public static class FileUsage {
@SerializedName("pageid") private int pageid;
@SerializedName("ns") private int ns;
@SerializedName("title") private String title;
public int pageId() {
return pageid;
}
public int ns() {
return ns;
}
public String title() {
return title;
}
}
public static class Category {
private int ns;
@SuppressWarnings("unused,NullableProblems") @Nullable private String title;
private boolean hidden;
public int ns() {
return ns;
}
@NonNull public String title() {
return StringUtils.defaultString(title);
}
public boolean hidden() {
return hidden;
}
}
}

View file

@ -0,0 +1,186 @@
package fr.free.nrw.commons.wikidata.mwapi
import androidx.annotation.VisibleForTesting
import com.google.gson.annotations.SerializedName
import fr.free.nrw.commons.wikidata.model.BaseModel
import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.GlobalUsage
/**
* A class representing a standard page object as returned by the MediaWiki API.
*/
class MwQueryPage : BaseModel() {
private val pageid = 0
private val index = 0
private var title: String? = null
private val categoryinfo: CategoryInfo? = null
private val revisions: List<Revision>? = null
@SerializedName("fileusage")
private val fileUsages: List<FileUsage>? = null
@SerializedName("globalusage")
private val globalUsages: List<GlobalUsage>? = null
private val coordinates: MutableList<Coordinates?>? = null
private val categories: List<Category>? = null
private val thumbnail: Thumbnail? = null
private val description: String? = null
@SerializedName("imageinfo")
private val imageInfo: List<ImageInfo>? = null
private var redirectFrom: String? = null
private var convertedFrom: String? = null
private var convertedTo: String? = null
fun title(): String = title!!
fun categoryInfo(): CategoryInfo = categoryinfo!!
fun index(): Int = index
fun revisions(): List<Revision>? = revisions
fun categories(): List<Category>? = categories
// TODO: Handle null values in lists during deserialization, perhaps with a new
// @RequiredElements annotation and corresponding TypeAdapter
fun coordinates(): List<Coordinates?>? = coordinates?.filterNotNull()
fun pageId(): Int = pageid
fun thumbUrl(): String? = thumbnail?.source()
fun description(): String? = description
fun imageInfo(): ImageInfo? = imageInfo?.get(0)
fun redirectFrom(from: String?) {
redirectFrom = from
}
fun convertedFrom(from: String?) {
convertedFrom = from
}
fun convertedTo(to: String?) {
convertedTo = to
}
fun appendTitleFragment(fragment: String?) {
title += "#$fragment"
}
fun checkWhetherFileIsUsedInWikis(): Boolean {
return checkWhetherFileIsUsedInWikis(globalUsages, fileUsages)
}
class Revision {
@SerializedName("revid")
private val revisionId: Long = 0
private val user: String? = null
@SerializedName("contentformat")
private val contentFormat: String? = null
@SerializedName("contentmodel")
private val contentModel: String? = null
@SerializedName("timestamp")
private val timeStamp: String? = null
private val content: String? = null
fun revisionId(): Long = revisionId
fun user(): String = user ?: ""
fun content(): String = content!!
fun timeStamp(): String = timeStamp ?: ""
}
class Coordinates {
private val lat: Double? = null
private val lon: Double? = null
fun lat(): Double? = lat
fun lon(): Double? = lon
}
class CategoryInfo {
val isHidden: Boolean = false
private val size = 0
private val pages = 0
private val files = 0
private val subcats = 0
}
internal class Thumbnail {
private val source: String? = null
private val width = 0
private val height = 0
fun source(): String? = source
}
class GlobalUsage {
@SerializedName("title")
val title: String? = null
@SerializedName("wiki")
val wiki: String? = null
@SerializedName("url")
val url: String? = null
}
class FileUsage {
@SerializedName("pageid")
private val pageid = 0
@SerializedName("ns")
private val ns = 0
@SerializedName("title")
private var title: String? = null
fun pageId(): Int = pageid
fun ns(): Int = ns
fun title(): String = title ?: ""
fun setTitle(value: String) {
title = value
}
}
class Category {
private val ns = 0
private val title: String? = null
private val hidden = false
fun ns(): Int = ns
fun title(): String = title ?: ""
fun hidden(): Boolean = hidden
}
}
@VisibleForTesting
fun checkWhetherFileIsUsedInWikis(
globalUsages: List<GlobalUsage>?,
fileUsages: List<MwQueryPage.FileUsage>?
): Boolean {
if (!globalUsages.isNullOrEmpty()) {
return true
}
if (fileUsages.isNullOrEmpty()) {
return false
}
/* Ignore usage under https://commons.wikimedia.org/wiki/User:Didym/Mobile_upload/
which has been a gallery of all of our uploads since 2014 */
return fileUsages.filterNot {
it.title().contains("User:Didym/Mobile upload")
}.isNotEmpty()
}

View file

@ -1,26 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
import java.util.Map;
public class MwQueryResponse extends MwResponse {
@SerializedName("continue") @Nullable private Map<String, String> continuation;
@SerializedName("query") @Nullable private MwQueryResult query;
@Nullable public Map<String, String> continuation() {
return continuation;
}
@Nullable public MwQueryResult query() {
return query;
}
public boolean success() {
return query != null;
}
}

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.wikidata.mwapi
import com.google.gson.annotations.SerializedName
class MwQueryResponse : MwResponse() {
@SerializedName("continue")
private val continuation: Map<String, String>? = null
private val query: MwQueryResult? = null
fun continuation(): Map<String, String>? = continuation
fun query(): MwQueryResult? = query
fun success(): Boolean = query != null
}

View file

@ -1,187 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
import fr.free.nrw.commons.wikidata.json.PostProcessingTypeAdapter;
import org.apache.commons.lang3.StringUtils;
import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo;
import fr.free.nrw.commons.wikidata.model.BaseModel;
import fr.free.nrw.commons.wikidata.model.notifications.Notification;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MwQueryResult extends BaseModel implements PostProcessingTypeAdapter.PostProcessable {
@SerializedName("pages") @Nullable private List<MwQueryPage> pages;
@Nullable private List<Redirect> redirects;
@Nullable private List<ConvertedTitle> converted;
@SerializedName("userinfo") private UserInfo userInfo;
@Nullable private List<ListUserResponse> users;
@Nullable private Tokens tokens;
@Nullable private NotificationList notifications;
@SerializedName("allimages") @Nullable private List<ImageDetails> allImages;
@Nullable public List<MwQueryPage> pages() {
return pages;
}
@Nullable public MwQueryPage firstPage() {
if (pages != null && pages.size() > 0) {
return pages.get(0);
}
return null;
}
@NonNull
public List<ImageDetails> allImages() {
return allImages == null ? Collections.emptyList() : allImages;
}
@Nullable public UserInfo userInfo() {
return userInfo;
}
@Nullable public String csrfToken() {
return tokens != null ? tokens.csrf() : null;
}
@Nullable public String loginToken() {
return tokens != null ? tokens.login() : null;
}
@Nullable public NotificationList notifications() {
return notifications;
}
@Nullable public ListUserResponse getUserResponse(@NonNull String userName) {
if (users != null) {
for (ListUserResponse user : users) {
// MediaWiki user names are case sensitive, but the first letter is always capitalized.
if (StringUtils.capitalize(userName).equals(user.name())) {
return user;
}
}
}
return null;
}
@NonNull public Map<String, ImageInfo> images() {
Map<String, ImageInfo> result = new HashMap<>();
if (pages != null) {
for (MwQueryPage page : pages) {
if (page.imageInfo() != null) {
result.put(page.title(), page.imageInfo());
}
}
}
return result;
}
@Override
public void postProcess() {
resolveConvertedTitles();
resolveRedirectedTitles();
}
private void resolveRedirectedTitles() {
if (redirects == null || pages == null) {
return;
}
for (MwQueryPage page : pages) {
for (MwQueryResult.Redirect redirect : redirects) {
// TODO: Looks like result pages and redirects can also be matched on the "index"
// property. Confirm in the API docs and consider updating.
if (page.title().equals(redirect.to())) {
page.redirectFrom(redirect.from());
if (redirect.toFragment() != null) {
page.appendTitleFragment(redirect.toFragment());
}
}
}
}
}
private void resolveConvertedTitles() {
if (converted == null || pages == null) {
return;
}
// noinspection ConstantConditions
for (MwQueryResult.ConvertedTitle convertedTitle : converted) {
// noinspection ConstantConditions
for (MwQueryPage page : pages) {
if (page.title().equals(convertedTitle.to())) {
page.convertedFrom(convertedTitle.from());
page.convertedTo(convertedTitle.to());
}
}
}
}
private static class Redirect {
private int index;
@Nullable private String from;
@Nullable private String to;
@SerializedName("tofragment") @Nullable private String toFragment;
@Nullable public String to() {
return to;
}
@Nullable public String from() {
return from;
}
@Nullable public String toFragment() {
return toFragment;
}
}
public static class ConvertedTitle {
@Nullable private String from;
@Nullable private String to;
@Nullable public String to() {
return to;
}
@Nullable public String from() {
return from;
}
}
private static class Tokens {
@SuppressWarnings("unused,NullableProblems") @SerializedName("csrftoken")
@Nullable private String csrf;
@SuppressWarnings("unused,NullableProblems") @SerializedName("createaccounttoken")
@Nullable private String createAccount;
@SuppressWarnings("unused,NullableProblems") @SerializedName("logintoken")
@Nullable private String login;
@Nullable private String csrf() {
return csrf;
}
@Nullable private String createAccount() {
return createAccount;
}
@Nullable private String login() {
return login;
}
}
public static class NotificationList {
@Nullable
private List<Notification> list;
@Nullable
public List<Notification> list() {
return list;
}
}
}

View file

@ -0,0 +1,134 @@
package fr.free.nrw.commons.wikidata.mwapi
import com.google.gson.annotations.SerializedName
import fr.free.nrw.commons.wikidata.json.PostProcessingTypeAdapter.PostProcessable
import fr.free.nrw.commons.wikidata.model.BaseModel
import fr.free.nrw.commons.wikidata.model.gallery.ImageInfo
import fr.free.nrw.commons.wikidata.model.notifications.Notification
import org.apache.commons.lang3.StringUtils
class MwQueryResult : BaseModel(), PostProcessable {
private val pages: List<MwQueryPage>? = null
private val redirects: List<Redirect>? = null
private val converted: List<ConvertedTitle>? = null
@SerializedName("userinfo")
private val userInfo: UserInfo? = null
private val users: List<ListUserResponse>? = null
private val tokens: Tokens? = null
private val notifications: NotificationList? = null
@SerializedName("allimages")
private val allImages: List<ImageDetails>? = null
fun pages(): List<MwQueryPage>? = pages
fun firstPage(): MwQueryPage? = pages?.firstOrNull()
fun allImages(): List<ImageDetails> = allImages ?: emptyList()
fun userInfo(): UserInfo? = userInfo
fun csrfToken(): String? = tokens?.csrf()
fun loginToken(): String? = tokens?.login()
fun notifications(): NotificationList? = notifications
fun getUserResponse(userName: String): ListUserResponse? =
users?.find { StringUtils.capitalize(userName) == it.name() }
fun images() = buildMap {
pages?.forEach { page ->
page.imageInfo()?.let {
put(page.title(), it)
}
}
}
override fun postProcess() {
resolveConvertedTitles()
resolveRedirectedTitles()
}
private fun resolveRedirectedTitles() {
if (redirects == null || pages == null) {
return
}
pages.forEach { page ->
redirects.forEach { redirect ->
// TODO: Looks like result pages and redirects can also be matched on the "index"
// property. Confirm in the API docs and consider updating.
if (page.title() == redirect.to()) {
page.redirectFrom(redirect.from())
if (redirect.toFragment() != null) {
page.appendTitleFragment(redirect.toFragment())
}
}
}
}
}
private fun resolveConvertedTitles() {
if (converted == null || pages == null) {
return
}
converted.forEach { convertedTitle ->
pages.forEach { page ->
if (page.title() == convertedTitle.to()) {
page.convertedFrom(convertedTitle.from())
page.convertedTo(convertedTitle.to())
}
}
}
}
private class Redirect {
private val index = 0
private val from: String? = null
private val to: String? = null
@SerializedName("tofragment")
private val toFragment: String? = null
fun to(): String? = to
fun from(): String? = from
fun toFragment(): String? = toFragment
}
class ConvertedTitle {
private val from: String? = null
private val to: String? = null
fun to(): String? = to
fun from(): String? = from
}
private class Tokens {
@SerializedName("csrftoken")
private val csrf: String? = null
@SerializedName("createaccounttoken")
private val createAccount: String? = null
@SerializedName("logintoken")
private val login: String? = null
fun csrf(): String? = csrf
fun createAccount(): String? = createAccount
fun login(): String? = login
}
class NotificationList {
private val list: List<Notification>? = null
fun list(): List<Notification>? = list
}
}

View file

@ -1,23 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
import fr.free.nrw.commons.wikidata.json.PostProcessingTypeAdapter;
import fr.free.nrw.commons.wikidata.model.BaseModel;
import java.util.List;
public abstract class MwResponse extends BaseModel implements PostProcessingTypeAdapter.PostProcessable {
@SuppressWarnings({"unused"}) @Nullable private List<MwServiceError> errors;
@SuppressWarnings("unused,NullableProblems") @SerializedName("servedby") @NonNull private String servedBy;
@Override
public void postProcess() {
if (errors != null && !errors.isEmpty()) {
throw new MwException(errors.get(0), errors);
}
}
}

View file

@ -0,0 +1,18 @@
package fr.free.nrw.commons.wikidata.mwapi
import com.google.gson.annotations.SerializedName
import fr.free.nrw.commons.wikidata.json.PostProcessingTypeAdapter.PostProcessable
import fr.free.nrw.commons.wikidata.model.BaseModel
abstract class MwResponse : BaseModel(), PostProcessable {
private val errors: List<MwServiceError>? = null
@SerializedName("servedby")
private val servedBy: String? = null
override fun postProcess() {
if (!errors.isNullOrEmpty()) {
throw MwException(errors[0], errors)
}
}
}

View file

@ -1,29 +0,0 @@
package fr.free.nrw.commons.wikidata.mwapi;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import fr.free.nrw.commons.wikidata.model.BaseModel;
/**
* Gson POJO for a MediaWiki API error.
*/
public class MwServiceError extends BaseModel {
@Nullable private String code;
@Nullable private String text;
@NonNull public String getTitle() {
return StringUtils.defaultString(code);
}
@NonNull public String getDetails() {
return StringUtils.defaultString(text);
}
@Nullable
public String getCode() {
return code;
}
}

View file

@ -0,0 +1,18 @@
package fr.free.nrw.commons.wikidata.mwapi
import fr.free.nrw.commons.wikidata.model.BaseModel
import org.apache.commons.lang3.StringUtils
/**
* Gson POJO for a MediaWiki API error.
*/
class MwServiceError : BaseModel() {
val code: String? = null
private val text: String? = null
val title: String
get() = code ?: ""
val details: String
get() = text ?: ""
}

View file

@ -139,7 +139,7 @@ class ReviewControllerTest {
@Test
fun testSendThanks() {
shadowOf(Looper.getMainLooper()).idle()
whenever(firstRevision.revisionId).thenReturn(1)
whenever(firstRevision.revisionId()).thenReturn(1)
Whitebox.setInternalState(controller, "firstRevision", firstRevision)
controller.sendThanks(activity)
assertEquals(

View file

@ -95,11 +95,11 @@ class ReviewHelperTest {
@Test
fun getFirstRevisionOfFile() {
val rev1 = mock<MwQueryPage.Revision>()
whenever(rev1.user).thenReturn("TestUser")
whenever(rev1.revisionId).thenReturn(1L)
whenever(rev1.user()).thenReturn("TestUser")
whenever(rev1.revisionId()).thenReturn(1L)
val rev2 = mock<MwQueryPage.Revision>()
whenever(rev2.user).thenReturn("TestUser")
whenever(rev2.revisionId).thenReturn(2L)
whenever(rev2.user()).thenReturn("TestUser")
whenever(rev2.revisionId()).thenReturn(2L)
val page = setupMedia("Test.jpg", rev1, rev2)
whenever(mwQueryResult.firstPage()).thenReturn(page)
@ -107,7 +107,7 @@ class ReviewHelperTest {
val firstRevisionOfFile = reviewHelper.getFirstRevisionOfFile("Test.jpg").blockingFirst()
assertEquals(1, firstRevisionOfFile.revisionId)
assertEquals(1, firstRevisionOfFile.revisionId())
}
@Test

View file

@ -0,0 +1,48 @@
package fr.free.nrw.commons.wikidata.mwapi
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class MwQueryPageTest {
private val didymUsage = MwQueryPage.FileUsage().apply {
setTitle("User:Didym/Mobile upload")
}
@Test
fun checkWhetherFileIsUsedInWikis_nullGlobalUsages() {
assertFalse(checkWhetherFileIsUsedInWikis(null, null))
}
@Test
fun checkWhetherFileIsUsedInWikis_emptyGlobalUsages() {
assertFalse(checkWhetherFileIsUsedInWikis(emptyList(), null))
}
@Test
fun checkWhetherFileIsUsedInWikis_emptyFileUsage() {
assertFalse(checkWhetherFileIsUsedInWikis(emptyList(), emptyList()))
}
@Test
fun checkWhetherFileIsUsedInWikis_singleGlobalUsages() {
assertTrue(checkWhetherFileIsUsedInWikis(listOf(MwQueryPage.GlobalUsage()), null))
}
@Test
fun checkWhetherFileIsUsedInWikis_singleFileUsageContainsDidym() {
assertFalse(checkWhetherFileIsUsedInWikis(null, listOf(didymUsage)))
}
@Test
fun checkWhetherFileIsUsedInWikis_didymIgnoredInList() {
assertTrue(
checkWhetherFileIsUsedInWikis(
null, listOf(
didymUsage, MwQueryPage.FileUsage().apply { setTitle("somewhere else") }
)
)
)
}
}