diff --git a/build.gradle b/build.gradle index f51df2b2..5cb2900a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - kotlinVersion = '1.3.72' + kotlinVersion = '1.4.20' moshiVersion = "1.9.2" } @@ -9,9 +9,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion" } } diff --git a/gradle.properties b/gradle.properties index 946d709d..53ae0ae4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ android.enableJetifier=true android.useAndroidX=true -org.gradle.jvmargs=-Xmx1536M \ No newline at end of file +org.gradle.jvmargs=-Xmx1536M diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index 8aac2c83..0a003a08 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -1,25 +1,20 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' dependencies { api 'com.squareup.okhttp3:okhttp:4.6.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5' - api 'com.github.AppDevNext.Logcat:LogcatCore:2.1.1' - api 'net.openid:appauth:0.7.1' + api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2' // Moshi implementation ("com.squareup.moshi:moshi-kotlin:$moshiVersion") { exclude module: "kotlin-reflect" } kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" -} -allOpen { - // allows mocking for classes w/o directly opening them for release builds - annotation 'com.owncloud.android.lib.testing.OpenClass' + testImplementation 'junit:junit:4.13.1' } android { @@ -29,12 +24,8 @@ android { minSdkVersion 21 targetSdkVersion 29 - versionCode = 10000800 - versionName = "1.0.8" - - // This is pretty ugly but manifest placeholders don't seem to work very well when using different modules - // See https://github.com/openid/AppAuth-Android/issues/325 - manifestPlaceholders = [appAuthRedirectScheme: ''] + versionCode = 10000900 + versionName = "1.0.9" } lintOptions { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index 12571a88..06e8056d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -57,6 +57,7 @@ public class HttpClient { private static OkHttpClient sOkHttpClient; private static Context sContext; private static HashMap> sCookieStore = new HashMap<>(); + private static LogInterceptor sLogInterceptor; public static OkHttpClient getOkHttpClient() { if (sOkHttpClient == null) { @@ -110,6 +111,7 @@ public class HttpClient { }; OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() + .addNetworkInterceptor(getLogInterceptor()) .protocols(Arrays.asList(Protocol.HTTP_1_1)) .readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) @@ -120,6 +122,7 @@ public class HttpClient { .cookieJar(cookieJar); // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. //.hostnameVerifier(new BrowserCompatHostnameVerifier()); + sOkHttpClient = clientBuilder.build(); } catch (Exception e) { @@ -137,6 +140,13 @@ public class HttpClient { sContext = context; } + public static LogInterceptor getLogInterceptor() { + if (sLogInterceptor == null) { + sLogInterceptor = new LogInterceptor(); + } + return sLogInterceptor; + } + public List getCookiesFromUrl(HttpUrl httpUrl) { return sCookieStore.get(httpUrl.host()); } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java index 3850cd3f..b86534a3 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java @@ -51,6 +51,14 @@ public class HttpConstants { public static final String ACCEPT_ENCODING_IDENTITY = "identity"; public static final String OC_FILE_REMOTE_ID = "OC-FileId"; + /*********************************************************************************************************** + ************************************************ CONTENT TYPES ******************************************** + ***********************************************************************************************************/ + + public static final String CONTENT_TYPE_XML = "application/xml"; + public static final String CONTENT_TYPE_JSON = "application/json"; + public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded"; + /*********************************************************************************************************** ************************************************ STATUS CODES ********************************************* ***********************************************************************************************************/ diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogBuilder.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogBuilder.kt new file mode 100644 index 00000000..c0ca4fb0 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogBuilder.kt @@ -0,0 +1,65 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.common.http + +import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON +import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_WWW_FORM +import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_XML +import okhttp3.MediaType +import timber.log.Timber +import java.util.Locale + +object LogBuilder { + fun logHttp( + networkPetition: NetworkPetition, + networkNode: NetworkNode, + requestId: String? = "", + description: String + ) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description") +} + +enum class NetworkPetition { + REQUEST, RESPONSE; + + override fun toString(): String = super.toString().toLowerCase(Locale.ROOT) +} + +enum class NetworkNode { + INFO, HEADER, BODY; + + override fun toString(): String = super.toString().toLowerCase(Locale.ROOT) +} + +/** + * Check whether a media type is loggable. + * + * @return true if its type is text, xml, json, or x-www-form-urlencoded. + */ +fun MediaType?.isLoggable(): Boolean = + this?.let { mediaType -> + val mediaTypeString = mediaType.toString() + (mediaType.type == "text" || + mediaTypeString.contains(CONTENT_TYPE_XML) || + mediaTypeString.contains(CONTENT_TYPE_JSON) || + mediaTypeString.contains(CONTENT_TYPE_WWW_FORM)) + } ?: false diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt new file mode 100644 index 00000000..0ca90a47 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/LogInterceptor.kt @@ -0,0 +1,179 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.common.http + +import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER +import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID +import com.owncloud.android.lib.common.http.LogBuilder.logHttp +import com.owncloud.android.lib.common.http.NetworkNode.BODY +import com.owncloud.android.lib.common.http.NetworkNode.HEADER +import com.owncloud.android.lib.common.http.NetworkNode.INFO +import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST +import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE +import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.RequestBody +import okhttp3.Response +import okhttp3.ResponseBody +import okio.Buffer +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets +import kotlin.math.max + +class LogInterceptor : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + + if (!httpLogsEnabled) { + return chain.proceed(chain.request()) + } + + val request = chain.request().also { + val requestId = it.headers[OC_X_REQUEST_ID] + logHttp(REQUEST, INFO, requestId, "Type: ${it.method} URL: ${it.url}") + logHeaders(requestId, it.headers, REQUEST) + logRequestBody(requestId, it.body) + } + + val response = chain.proceed(request) + + return response.also { + val requestId = it.request.headers[OC_X_REQUEST_ID] + logHttp( + RESPONSE, + INFO, + requestId, + "Code: ${it.code} Message: ${it.message} IsSuccessful: ${it.isSuccessful}" + ) + logHeaders(requestId, it.headers, RESPONSE) + logResponseBody(requestId, it.body) + } + } + + private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) { + headers.forEach { header -> + val headerValue: String = if (header.first.equals(AUTHORIZATION_HEADER, true)) { + "[redacted]" + } else { + header.second + } + logHttp(networkPetition, HEADER, requestId, "${header.first}: $headerValue") + } + } + + private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) { + requestBodyParam?.let { requestBody -> + + if (requestBody.isOneShot()) { + logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted") + return@let + } + + if (requestBody.isDuplex()) { + logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted") + return@let + } + + val buffer = Buffer() + requestBody.writeTo(buffer) + + val contentType = requestBody.contentType() + val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 + + logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body") + logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}") + logHttp(REQUEST, BODY, requestId, "--> Body start for request") + + if (contentType.isLoggable()) { + if (requestBody.contentLength() < LIMIT_BODY_LOG) { + logHttp(REQUEST, BODY, requestId, buffer.readString(charset)) + } else { + logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset)) + } + logHttp( + REQUEST, + BODY, + requestId, + "<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes" + ) + } else { + logHttp( + REQUEST, + BODY, + requestId, + "<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes" + ) + } + + } ?: logHttp(REQUEST, BODY, requestId, "Empty body") + } + + private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) { + responseBodyParam?.let { responseBody -> + + val contentType = responseBody.contentType() + val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8 + + logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body") + logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}") + logHttp(RESPONSE, BODY, requestId, "--> Body start for response") + + val source = responseBody.source() + source.request(LIMIT_BODY_LOG) + val buffer = source.buffer + + if (contentType.isLoggable()) { + + if (responseBody.contentLength() < LIMIT_BODY_LOG) { + logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(charset)) + } else { + logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset)) + } + logHttp( + RESPONSE, + BODY, + requestId, + "<-- Body end for response -- Omitted: ${ + max( + 0, + responseBody.contentLength() - LIMIT_BODY_LOG + ) + } bytes" + ) + } else { + logHttp( + RESPONSE, + BODY, + requestId, + "<-- Body end for response -- Binary -- Omitted: ${responseBody.contentLength()} bytes" + ) + } + } ?: logHttp(RESPONSE, BODY, requestId, "Empty body") + } + + companion object { + var httpLogsEnabled: Boolean = false + private const val LIMIT_BODY_LOG: Long = 1024 + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java index 53abaf9a..40f168ea 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java @@ -84,18 +84,12 @@ public class ChunkFromFileRequestBody extends FileRequestBody { long maxCount = Math.min(mOffset + mChunkSize, mChannel.size()); while (mChannel.position() < maxCount) { - Timber.v("Sink buffer size: %s", sink.buffer().size()); - readCount = mChannel.read(mBuffer); - Timber.v("Read " + readCount + " bytes from file channel to " + mBuffer.toString()); - - sink.buffer().write(mBuffer.array(), 0, readCount); + sink.getBuffer().write(mBuffer.array(), 0, readCount); sink.flush(); - Timber.v("Write " + readCount + " bytes to sink buffer with size " + sink.buffer().size()); - mBuffer.clear(); if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks mTransferred += readCount; diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java index 2df9884d..18746ba7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java @@ -53,6 +53,11 @@ public class FileRequestBody extends RequestBody implements ProgressiveDataTrans mContentType = contentType; } + @Override + public boolean isOneShot() { + return true; + } + @Override public MediaType contentType() { return mContentType; diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CommonOcsResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt similarity index 86% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CommonOcsResponse.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt index d8402eb9..7a37dc82 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CommonOcsResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt @@ -21,8 +21,9 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.owncloud.android.lib.resources.response +package com.owncloud.android.lib.resources +import com.squareup.moshi.Json import com.squareup.moshi.JsonClass // Response retrieved by OCS Rest API, used to obtain capabilities, shares and user info among others. @@ -41,6 +42,11 @@ data class OCSResponse( @JsonClass(generateAdapter = true) data class MetaData( val status: String, - val statuscode: Int, - val message: String? + @Json(name = "statuscode") + val statusCode: Int, + val message: String?, + @Json(name = "itemsperpage") + val itemsPerPage: String?, + @Json(name = "totalitems") + val totalItems: String? ) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java index 481ca168..cc159611 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java @@ -30,6 +30,8 @@ import java.io.File; public class FileUtils { public static final String FINAL_CHUNKS_FILE = ".file"; + public static final String MIME_DIR = "DIR"; + public static final String MIME_DIR_UNIX = "httpd/unix-directory"; static String getParentPath(String remotePath) { String parentPath = new File(remotePath).getParent(); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java index cb390993..0557f99e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java @@ -29,7 +29,17 @@ import android.os.Parcelable; import at.bitfire.dav4jvm.Property; import at.bitfire.dav4jvm.Response; -import at.bitfire.dav4jvm.property.*; +import at.bitfire.dav4jvm.property.CreationDate; +import at.bitfire.dav4jvm.property.GetContentLength; +import at.bitfire.dav4jvm.property.GetContentType; +import at.bitfire.dav4jvm.property.GetETag; +import at.bitfire.dav4jvm.property.GetLastModified; +import at.bitfire.dav4jvm.property.OCId; +import at.bitfire.dav4jvm.property.OCPermissions; +import at.bitfire.dav4jvm.property.OCPrivatelink; +import at.bitfire.dav4jvm.property.OCSize; +import at.bitfire.dav4jvm.property.QuotaAvailableBytes; +import at.bitfire.dav4jvm.property.QuotaUsedBytes; import java.io.File; import java.io.Serializable; @@ -83,7 +93,8 @@ public class RemoteFile implements Parcelable, Serializable { /** * Create new {@link RemoteFile} with given path. *

- * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'. + * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first + * character in 'path'. * * @param path The remote path of the file. */ @@ -95,7 +106,7 @@ public class RemoteFile implements Parcelable, Serializable { mRemotePath = path; mCreationTimestamp = 0; mLength = 0; - mMimeType = "DIR"; + mMimeType = FileUtils.MIME_DIR; mQuotaUsedBytes = BigDecimal.ZERO; mQuotaAvailableBytes = BigDecimal.ZERO; mPrivateLink = null; @@ -154,6 +165,14 @@ public class RemoteFile implements Parcelable, Serializable { readFromParcel(source); } + /** + * Use this to find out if this file is a folder. + * + * @return true if it is a folder + */ + public boolean isFolder() { + return mMimeType != null && (mMimeType.equals(FileUtils.MIME_DIR) || mMimeType.equals(FileUtils.MIME_DIR_UNIX)); + } /** * Getters and Setters diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareOperation.java index 4b815df1..9e71252e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareOperation.java @@ -46,9 +46,9 @@ import java.net.URL; public class GetRemoteShareOperation extends RemoteOperation { - private long mRemoteId; + private String mRemoteId; - public GetRemoteShareOperation(long remoteId) { + public GetRemoteShareOperation(String remoteId) { mRemoteId = remoteId; } @@ -60,7 +60,7 @@ public class GetRemoteShareOperation extends RemoteOperation Uri requestUri = client.getBaseUri(); Uri.Builder uriBuilder = requestUri.buildUpon(); uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH); - uriBuilder.appendEncodedPath(Long.toString(mRemoteId)); + uriBuilder.appendEncodedPath(mRemoteId); GetMethod getMethod = new GetMethod(new URL(uriBuilder.build().toString())); getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt index e3432b5b..546bf05b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt @@ -1,5 +1,6 @@ /* ownCloud Android Library is available under MIT license * + * @author Christian Schabesberger * @author masensio * @author David A. Velasco * @author David González Verdugo @@ -28,16 +29,21 @@ package com.owncloud.android.lib.resources.shares +import android.net.Uri import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK -import org.json.JSONObject +import com.owncloud.android.lib.resources.CommonOcsResponse +import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import timber.log.Timber +import java.lang.reflect.Type import java.net.URL -import java.util.ArrayList /** * Created by masensio on 08/10/2015. @@ -65,6 +71,7 @@ import java.util.ArrayList * Status codes: * 100 - successful * + * @author Christian Schabesberger * @author masensio * @author David A. Velasco * @author David González Verdugo @@ -78,80 +85,66 @@ class GetRemoteShareesOperation * @param perPage maximum number of results in a single page */ (private val searchString: String, private val page: Int, private val perPage: Int) : - RemoteOperation>() { + RemoteOperation() { - override fun run(client: OwnCloudClient): RemoteOperationResult> { - var result: RemoteOperationResult> + private fun buildRequestUri(baseUri: Uri) = + baseUri.buildUpon() + .appendEncodedPath(OCS_ROUTE) + .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) + .appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE) + .appendQueryParameter(PARAM_SEARCH, searchString) + .appendQueryParameter(PARAM_PAGE, page.toString()) + .appendQueryParameter(PARAM_PER_PAGE, perPage.toString()) + .build() - try { - val requestUri = client.baseUri - val uriBuilder = requestUri.buildUpon() - .appendEncodedPath(OCS_ROUTE) - .appendQueryParameter(PARAM_FORMAT, VALUE_FORMAT) - .appendQueryParameter(PARAM_ITEM_TYPE, VALUE_ITEM_TYPE) - .appendQueryParameter(PARAM_SEARCH, searchString) - .appendQueryParameter(PARAM_PAGE, page.toString()) - .appendQueryParameter(PARAM_PER_PAGE, perPage.toString()) + private fun parseResponse(response: String): ShareeOcsResponse? { + val moshi = Moshi.Builder().build() + val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java) + val adapter: JsonAdapter> = moshi.adapter(type) + return adapter.fromJson(response)!!.ocs.data + } - val getMethod = GetMethod(URL(uriBuilder.build().toString())) + private fun onResultUnsuccessful( + method: GetMethod, + response: String?, + status: Int + ): RemoteOperationResult { + Timber.e("Failed response while getting users/groups from the server ") + if (response != null) { + Timber.e("*** status code: $status; response message: $response") + } else { + Timber.e("*** status code: $status") + } + return RemoteOperationResult(method) + } - getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) + private fun onRequestSuccessful(response: String?): RemoteOperationResult { + val result = RemoteOperationResult(OK) + Timber.d("Successful response: $response") + result.data = parseResponse(response!!) + Timber.d("*** Get Users or groups completed ") + return result + } + override fun run(client: OwnCloudClient): RemoteOperationResult { + val requestUri = buildRequestUri(client.baseUri) + + val getMethod = GetMethod(URL(requestUri.toString())) + getMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE) + + return try { val status = client.executeHttpMethod(getMethod) val response = getMethod.getResponseBodyAsString() - if (isSuccess(status)) { - Timber.d("Successful response: $response") - - // Parse the response - val respJSON = JSONObject(response) - val respOCS = respJSON.getJSONObject(NODE_OCS) - val respData = respOCS.getJSONObject(NODE_DATA) - val respExact = respData.getJSONObject(NODE_EXACT) - val respExactUsers = respExact.getJSONArray(NODE_USERS) - val respExactGroups = respExact.getJSONArray(NODE_GROUPS) - val respExactRemotes = respExact.getJSONArray(NODE_REMOTES) - val respPartialUsers = respData.getJSONArray(NODE_USERS) - val respPartialGroups = respData.getJSONArray(NODE_GROUPS) - val respPartialRemotes = respData.getJSONArray(NODE_REMOTES) - val jsonResults = arrayOf( - respExactUsers, - respExactGroups, - respExactRemotes, - respPartialUsers, - respPartialGroups, - respPartialRemotes - ) - - val data = ArrayList() // For result data - for (i in 0..5) { - for (j in 0 until jsonResults[i].length()) { - val jsonResult = jsonResults[i].getJSONObject(j) - data.add(jsonResult) - Timber.d("*** Added item: ${jsonResult.getString(PROPERTY_LABEL)}") - } - } - - result = RemoteOperationResult(OK) - result.data = data - - Timber.d("*** Get Users or groups completed ") - + if (!isSuccess(status)) { + onResultUnsuccessful(getMethod, response, status) } else { - result = RemoteOperationResult(getMethod) - Timber.e("Failed response while getting users/groups from the server ") - if (response != null) { - Timber.e("*** status code: $status; response message: $response") - } else { - Timber.e("*** status code: $status") - } + onRequestSuccessful(response) } } catch (e: Exception) { - result = RemoteOperationResult(e) Timber.e(e, "Exception while getting users/groups") + RemoteOperationResult(e) } - - return result } private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK @@ -171,18 +164,5 @@ class GetRemoteShareesOperation // Arguments - constant values private const val VALUE_FORMAT = "json" private const val VALUE_ITEM_TYPE = "file" // to get the server search for users / groups - - // JSON Node names - private const val NODE_OCS = "ocs" - private const val NODE_DATA = "data" - private const val NODE_EXACT = "exact" - private const val NODE_USERS = "users" - private const val NODE_GROUPS = "groups" - private const val NODE_REMOTES = "remotes" - const val NODE_VALUE = "value" - const val PROPERTY_LABEL = "label" - const val PROPERTY_SHARE_TYPE = "shareType" - const val PROPERTY_SHARE_WITH = "shareWith" - const val PROPERTY_SHARE_WITH_ADDITIONAL_INFO = "shareWithAdditionalInfo" } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoteShare.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoteShare.kt index 20bb1ae9..320da5fc 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoteShare.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoteShare.kt @@ -24,7 +24,6 @@ package com.owncloud.android.lib.resources.shares -import com.owncloud.android.lib.resources.files.FileUtils import java.io.File /** @@ -35,7 +34,7 @@ import java.io.File * @author David González Verdugo */ data class RemoteShare( - var id: Long = 0, + var id: String = "0", var shareWith: String = "", var path: String = "", var token: String = "", @@ -43,15 +42,11 @@ data class RemoteShare( var sharedWithAdditionalInfo: String = "", var name: String = "", var shareLink: String = "", - var fileSource: String = "0", - var itemSource: String = "0", var shareType: ShareType? = ShareType.UNKNOWN, var permissions: Int = DEFAULT_PERMISSION, var sharedDate: Long = INIT_SHARED_DATE, var expirationDate: Long = INIT_EXPIRATION_DATE_IN_MILLIS, - var isFolder: Boolean = path.endsWith(File.separator), - var userId: Long = 0, - val isValid: Boolean = id > -1 + var isFolder: Boolean = path.endsWith(File.separator) ) { companion object { @@ -106,17 +101,6 @@ enum class ShareType constructor(val value: Int) { FEDERATED(6); companion object { - fun fromValue(value: Int): ShareType? { - return when (value) { - -1 -> UNKNOWN - 0 -> USER - 1 -> GROUP - 3 -> PUBLIC_LINK - 4 -> EMAIL - 5 -> CONTACT - 6 -> FEDERATED - else -> null - } - } + fun fromValue(value: Int) = values().firstOrNull { it.value == value } } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt index 5fa18169..a7d18e5d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/RemoveRemoteShareOperation.kt @@ -48,7 +48,7 @@ import java.net.URL * * @param remoteShareId Share ID */ -class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperation() { +class RemoveRemoteShareOperation(private val remoteShareId: String) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { var result: RemoteOperationResult @@ -57,7 +57,7 @@ class RemoveRemoteShareOperation(private val remoteShareId: Long) : RemoteOperat val requestUri = client.baseUri val uriBuilder = requestUri.buildUpon() uriBuilder.appendEncodedPath(ShareUtils.SHARING_API_PATH) - uriBuilder.appendEncodedPath(remoteShareId.toString()) + uriBuilder.appendEncodedPath(remoteShareId) val deleteMethod = DeleteMethod( URL(uriBuilder.build().toString()) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/ShareXMLParser.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/ShareXMLParser.kt index a63c35a7..389d6f3f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/ShareXMLParser.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/ShareXMLParser.kt @@ -179,7 +179,7 @@ class ShareXMLParser { name.equals(NODE_ID, ignoreCase = true) -> {// Parse Create XML Response share = RemoteShare() val value = readNode(parser, NODE_ID) - share.id = Integer.parseInt(value).toLong() + share.id = value } name.equals(NODE_URL, ignoreCase = true) -> { // NOTE: this field is received in all the public shares from OC 9.0.0 @@ -236,7 +236,7 @@ class ShareXMLParser { } name.equals(NODE_ID, ignoreCase = true) -> { - remoteShare.id = Integer.parseInt(readNode(parser, NODE_ID)).toLong() + remoteShare.id = readNode(parser, NODE_ID) } name.equals(NODE_ITEM_TYPE, ignoreCase = true) -> { @@ -244,10 +244,6 @@ class ShareXMLParser { fixPathForFolder(remoteShare) } - name.equals(NODE_ITEM_SOURCE, ignoreCase = true) -> { - remoteShare.itemSource = readNode(parser, NODE_ITEM_SOURCE) - } - name.equals(NODE_PARENT, ignoreCase = true) -> { readNode(parser, NODE_PARENT) } @@ -261,10 +257,6 @@ class ShareXMLParser { remoteShare.shareWith = readNode(parser, NODE_SHARE_WITH) } - name.equals(NODE_FILE_SOURCE, ignoreCase = true) -> { - remoteShare.fileSource = readNode(parser, NODE_FILE_SOURCE) - } - name.equals(NODE_PATH, ignoreCase = true) -> { remoteShare.path = readNode(parser, NODE_PATH) fixPathForFolder(remoteShare) @@ -320,9 +312,7 @@ class ShareXMLParser { } } - if (remoteShare.isValid) { - shares.add(remoteShare) - } + shares.add(remoteShare) } private fun fixPathForFolder(share: RemoteShare) { @@ -403,11 +393,9 @@ class ShareXMLParser { private const val NODE_ELEMENT = "element" private const val NODE_ID = "id" private const val NODE_ITEM_TYPE = "item_type" - private const val NODE_ITEM_SOURCE = "item_source" private const val NODE_PARENT = "parent" private const val NODE_SHARE_TYPE = "share_type" private const val NODE_SHARE_WITH = "share_with" - private const val NODE_FILE_SOURCE = "file_source" private const val NODE_PATH = "path" private const val NODE_PERMISSIONS = "permissions" private const val NODE_STIME = "stime" diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt index e2af8959..0df80bc9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/UpdateRemoteShareOperation.kt @@ -55,7 +55,7 @@ class UpdateRemoteShareOperation /** * @param remoteId Identifier of the share to update. */ - private val remoteId: Long + private val remoteId: String ) : RemoteOperation() { /** diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponse.kt new file mode 100644 index 00000000..0cd0162a --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponse.kt @@ -0,0 +1,72 @@ +/* ownCloud Android Library is available under MIT license + * + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.owncloud.android.lib.resources.shares.responses + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This was modeled according to the documentation: + * https://doc.owncloud.com/server/developer_manual/core/apis/ocs-recipient-api.html#get-shares-recipients + */ +@JsonClass(generateAdapter = true) +data class ShareeOcsResponse( + val exact: ExactSharees?, + val groups: List, + val remotes: List, + val users: List +) { + fun getFlatRepresentationWithoutExact() = ArrayList().apply { + addAll(users) + addAll(remotes) + addAll(groups) + } +} + +@JsonClass(generateAdapter = true) +data class ExactSharees( + val groups: List, + val remotes: List, + val users: List +) { + fun getFlatRepresentation() = ArrayList().apply { + addAll(users) + addAll(remotes) + addAll(groups) + } +} + +@JsonClass(generateAdapter = true) +data class ShareeItem( + val label: String, + val value: ShareeValue +) + +@JsonClass(generateAdapter = true) +data class ShareeValue( + val shareType: Int, + val shareWith: String, + @Json(name = "shareWithAdditionalInfo") + val additionalInfo: String? +) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt index 38980256..caca3cd8 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareService.kt @@ -44,7 +44,7 @@ interface ShareService : Service { ): RemoteOperationResult fun updateShare( - remoteId: Long, + remoteId: String, name: String, password: String?, expirationDate: Long, @@ -52,5 +52,5 @@ interface ShareService : Service { publicUpload: Boolean ): RemoteOperationResult - fun deleteShare(remoteId: Long): RemoteOperationResult + fun deleteShare(remoteId: String): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt index ab6d9e83..95983ec5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/ShareeService.kt @@ -1,6 +1,7 @@ /** * ownCloud Android client application * + * @author Christian Schabesberger * @author David González Verdugo * * Copyright (C) 2020 ownCloud GmbH. @@ -22,13 +23,12 @@ package com.owncloud.android.lib.resources.shares.services import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.Service -import org.json.JSONObject -import java.util.ArrayList +import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse interface ShareeService : Service { fun getSharees( searchString: String, page: Int, perPage: Int - ): RemoteOperationResult> + ): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt index a22919bf..109c2204 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareService.kt @@ -66,7 +66,7 @@ class OCShareService(override val client: OwnCloudClient) : }.execute(client) override fun updateShare( - remoteId: Long, + remoteId: String, name: String, password: String?, expirationDate: Long, @@ -84,7 +84,7 @@ class OCShareService(override val client: OwnCloudClient) : this.retrieveShareDetails = true }.execute(client) - override fun deleteShare(remoteId: Long): RemoteOperationResult = + override fun deleteShare(remoteId: String): RemoteOperationResult = RemoveRemoteShareOperation( remoteId ).execute(client) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt index 370ef34b..9cb014f3 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/services/implementation/OCShareeService.kt @@ -23,9 +23,8 @@ package com.owncloud.android.lib.resources.shares.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation +import com.owncloud.android.lib.resources.shares.responses.ShareeOcsResponse import com.owncloud.android.lib.resources.shares.services.ShareeService -import org.json.JSONObject -import java.util.ArrayList class OCShareeService(override val client: OwnCloudClient) : ShareeService { @@ -33,7 +32,7 @@ class OCShareeService(override val client: OwnCloudClient) : searchString: String, page: Int, perPage: Int - ): RemoteOperationResult> = + ): RemoteOperationResult = GetRemoteShareesOperation( searchString, page, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt index f9f164eb..806440be 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/GetRemoteCapabilitiesOperation.kt @@ -34,8 +34,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK -import com.owncloud.android.lib.resources.response.CapabilityResponse -import com.owncloud.android.lib.resources.response.CommonOcsResponse +import com.owncloud.android.lib.resources.status.responses.CapabilityResponse +import com.owncloud.android.lib.resources.CommonOcsResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CapabilityResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt similarity index 99% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CapabilityResponse.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt index 49efaea9..dea2431e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/CapabilityResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/status/responses/CapabilityResponse.kt @@ -22,7 +22,7 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.resources.response +package com.owncloud.android.lib.resources.status.responses import com.owncloud.android.lib.resources.status.RemoteCapability import com.owncloud.android.lib.resources.status.RemoteCapability.CapabilityBooleanType diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserInfoOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserInfoOperation.kt index 53600d29..d1f2a43c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserInfoOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/GetRemoteUserInfoOperation.kt @@ -29,8 +29,8 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.response.CommonOcsResponse -import com.owncloud.android.lib.resources.response.UserInfoResponse +import com.owncloud.android.lib.resources.CommonOcsResponse +import com.owncloud.android.lib.resources.users.responses.UserInfoResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/UserInfoResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/responses/UserInfoResponse.kt similarity index 96% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/UserInfoResponse.kt rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/responses/UserInfoResponse.kt index 1ff3b94e..0371e50f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/response/UserInfoResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/users/responses/UserInfoResponse.kt @@ -21,7 +21,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.owncloud.android.lib.resources.response +package com.owncloud.android.lib.resources.users.responses import com.owncloud.android.lib.resources.users.RemoteUserInfo import com.squareup.moshi.Json diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt new file mode 100644 index 00000000..ee88839d --- /dev/null +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt @@ -0,0 +1,93 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.shares.responses + +import com.owncloud.android.lib.resources.CommonOcsResponse +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import org.junit.Assert.assertNull +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import java.io.File +import java.lang.reflect.Type + +class ShareeResponseTest { + + lateinit var adapter: JsonAdapter> + + private fun loadResponses(fileName: String) = + adapter.fromJson(File(fileName).readText()) + + @Before + fun prepare() { + val moshi = Moshi.Builder().build() + val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java) + adapter = moshi.adapter(type) + } + + @Test + fun `check structure - ok - contains meta`() { + val response = loadResponses(EXAMPLE_RESPONSE_JSON)!! + assertEquals("OK", response.ocs.meta.message!!) + assertEquals(200, response.ocs.meta.statusCode) + assertEquals("ok", response.ocs.meta.status) + assertTrue(response.ocs.meta.itemsPerPage?.isEmpty()!!) + assertTrue(response.ocs.meta.totalItems?.isEmpty()!!) + } + + @Test + fun `example response - ok - correct sturcture`() { + val response = loadResponses(EXAMPLE_RESPONSE_JSON)!! + assertEquals(2, response.ocs.data.groups.size) + assertEquals(0, response.ocs.data.remotes.size) + assertEquals(2, response.ocs.data.users.size) + assertEquals(0, response.ocs.data.exact?.groups?.size) + assertEquals(0, response.ocs.data.exact?.remotes?.size) + assertEquals(1, response.ocs.data.exact?.users?.size) + assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo) + assertNull(response.ocs.data.users[1].value.additionalInfo) + } + + @Test + fun `check empty response - ok - parsing ok`() { + val response = loadResponses(EMPTY_RESPONSE_JSON)!! + assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!) + assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!) + assertTrue(response.ocs.data.exact?.users?.isEmpty()!!) + assertTrue(response.ocs.data.groups.isEmpty()) + assertTrue(response.ocs.data.remotes.isEmpty()) + assertTrue(response.ocs.data.users.isEmpty()) + } + + companion object { + val RESOURCES_PATH = + "src/test/responses/com.owncloud.android.lib.resources.sharees.responses" + val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json" + val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json" + } +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/empty_sharee_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/empty_sharee_response.json new file mode 100644 index 00000000..b74338d2 --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/empty_sharee_response.json @@ -0,0 +1,21 @@ +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 100, + "message": "OK", + "totalitems": "", + "itemsperpage": "" + }, + "data": { + "exact": { + "users": [], + "groups": [], + "remotes": [] + }, + "users": [], + "groups": [], + "remotes": [] + } + } +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/example_sharee_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/example_sharee_response.json new file mode 100644 index 00000000..fe988811 --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.sharees.responses/example_sharee_response.json @@ -0,0 +1,60 @@ +{ + "ocs": { + "data": { + "exact": { + "groups": [], + "remotes": [], + "users": [ + { + "label": "admin", + "value": { + "shareType": 0, + "shareWith": "admin" + } + } + ] + }, + "groups": [ + { + "label": "group1", + "value": { + "shareType": 1, + "shareWith": "group1" + } + }, + { + "label": "group2", + "value": { + "shareType": 1, + "shareWith": "group2" + } + } + ], + "remotes": [], + "users": [ + { + "label": "user1", + "value": { + "shareType": 0, + "shareWith": "user1", + "shareWithAdditionalInfo": "user1@user1.com" + } + }, + { + "label": "user2", + "value": { + "shareType": 0, + "shareWith": "user2" + } + } + ] + }, + "meta": { + "itemsperpage": "", + "message": "OK", + "status": "ok", + "statuscode": 200, + "totalitems": "" + } + } +} \ No newline at end of file