Skip to content

Commit

Permalink
Add client reporting
Browse files Browse the repository at this point in the history
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
  • Loading branch information
tobiasKaminsky committed Dec 7, 2023
1 parent cf3f1ae commit ec4a43e
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,6 @@
*/
package com.owncloud.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation;
Expand All @@ -41,6 +35,12 @@

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

/**
* Class to test GetRemoteCapabilitiesOperation
*/
Expand Down Expand Up @@ -150,12 +150,17 @@ private void checkCapability(OCCapability capability, String userId) {
// groupfolder
if (capability.getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_27)) {
if (userId.equals("test")) {
capability.getGroupfolders().isTrue();
assertTrue(capability.getGroupfolders().isTrue());
} else {
capability.getGroupfolders().isFalse();
assertTrue(capability.getGroupfolders().isFalse());
}
} else {
capability.getGroupfolders().isFalse();
assertTrue(capability.getGroupfolders().isFalse());
}

// security guard
if (capability.getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_28)) {
assertTrue(capability.getSecurityGuard().isFalse());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2023 Tobias Kaminsky
* Copyright (C) 2023 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

package com.owncloud.android.lib.resources.status

import com.owncloud.android.AbstractIT
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

@Suppress("Detekt.MagicNumber")
class SendClientDiagnosticRemoteOperationIT : AbstractIT() {
@Test
@Suppress("Detekt.MaxLineLength", "ktlint:standard:max-line-length")
fun testJSON() {
val problems =
listOf(
Problem("UploadResult.CREDENTIAL_ERROR", 2, 1700152062),
Problem("UploadResult.FOLDER_ERROR", 3, 1400652062)
)

val sut =
SendClientDiagnosticRemoteOperation(
Problem(SendClientDiagnosticRemoteOperation.SYNC_CONFLICTS, 1, 1700652062),
problems,
Problem(SendClientDiagnosticRemoteOperation.VIRUS_DETECTED, 4, 1234234234),
Problem(SendClientDiagnosticRemoteOperation.E2E_ERRORS, 2, 1700612062)
)
assertEquals(
"""{"sync_conflicts": {"count": 1, "oldest": 1700652062}, "problems": {"UploadResult.CREDENTIAL_ERROR": {"count": 2, "oldest": 1700152062}, "UploadResult.FOLDER_ERROR": {"count": 3, "oldest": 1400652062}}, "virus_detected": {"count": 4, "oldest": 1234234234}, "e2e_errors": {"count": 2, "oldest": 1700612062}}""",
sut.generateJSON()
)
}

@Test
fun testEmptyJSON() {
val sut =
SendClientDiagnosticRemoteOperation(
null,
null,
null,
null
)
assertEquals(
"""{"sync_conflicts": {}, "problems": {}, "virus_detected": {}, "e2e_errors": {}}""",
sut.generateJSON()
)
}

@Test
fun sendDiagnostic() {
val problems =
listOf(
Problem("UploadResult.CREDENTIAL_ERROR", 2, 1700152062),
Problem("UploadResult.FOLDER_ERROR", 3, 1400652062)
)

val sut =
SendClientDiagnosticRemoteOperation(
Problem(SendClientDiagnosticRemoteOperation.SYNC_CONFLICTS, 1, 1700652062),
problems,
null,
Problem(SendClientDiagnosticRemoteOperation.E2E_ERRORS, 2, 1700612062)
).execute(nextcloudClient)
assertTrue(sut.isSuccess) // we cannot check anything else
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public OwnCloudAccount(Account savedAccount, Context context) throws AccountNotF
name = savedAccount.name;
credentials = null; // load of credentials is delayed

AccountManager ama = AccountManager.get(context.getApplicationContext());
AccountManager ama = AccountManager.get(context);
String baseUrl = ama.getUserData(this.savedAccount, AccountUtils.Constants.KEY_OC_BASE_URL);
if (baseUrl == null) {
throw new AccountNotFoundException(this.savedAccount, "Account not found", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public static String constructBasicURLForAccount(Context context, Account accoun
*/
public static String getBaseUrlForAccount(Context context, Account account)
throws AccountNotFoundException {
AccountManager ama = AccountManager.get(context.getApplicationContext());
AccountManager ama = AccountManager.get(context);
String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);

if (baseurl == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@

import javax.net.ssl.SSLException;

import androidx.annotation.Nullable;
import okhttp3.Headers;


Expand Down Expand Up @@ -548,6 +549,7 @@ public ResultCode getCode() {
return mCode;
}

@Nullable
public Exception getException() {
return mException;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ public class GetCapabilitiesRemoteOperation extends RemoteOperation {
// drop-account
private static final String NODE_DROP_ACCOUNT = "drop-account";

// security guard
private static final String NODE_SECURITY_GUARD = "security_guard";
private static final String NODE_DIAGNOSTICS = "diagnostics";

private OCCapability currentCapability = null;

Expand Down Expand Up @@ -687,6 +690,19 @@ private OCCapability parseResponse(String response) throws JSONException {
capability.setDropAccount(CapabilityBooleanType.FALSE);
}
}

// security guard
if (respCapabilities.has(NODE_SECURITY_GUARD)) {
JSONObject securityGuardCapability = respCapabilities.getJSONObject(NODE_SECURITY_GUARD);

if (securityGuardCapability.getBoolean(NODE_DIAGNOSTICS)) {
capability.setSecurityGuard(CapabilityBooleanType.TRUE);
} else {
capability.setSecurityGuard(CapabilityBooleanType.FALSE);
}
} else {
capability.setSecurityGuard(CapabilityBooleanType.FALSE);
}
}

Log_OC.d(TAG, "*** Get Capabilities completed ");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class OCCapability {
// Drop-Account
var dropAccount = CapabilityBooleanType.UNKNOWN

// Security guard
var securityGuard = CapabilityBooleanType.UNKNOWN

// Etag for capabilities
var etag: String? = ""

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2023 Tobias Kaminsky
* Copyright (C) 2023 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

package com.owncloud.android.lib.resources.status

data class Problem(val type: String, val count: Int, val oldestTimestamp: Long) {
fun toJsonString(): String {
return """{"count": $count, "oldest": $oldestTimestamp}"""
}

fun toJsonWithTypeString(): String {
return """"$type": {"count": $count, "oldest": $oldestTimestamp}"""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* Nextcloud Android Library is available under MIT license
*
* @author Tobias Kaminsky
* Copyright (C) 2023 Tobias Kaminsky
* Copyright (C) 2023 Nextcloud 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.status

import androidx.annotation.VisibleForTesting
import com.google.gson.Gson
import com.nextcloud.common.NextcloudClient
import com.nextcloud.operations.PutMethod
import com.owncloud.android.lib.common.operations.RemoteOperation
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import org.apache.commons.httpclient.HttpStatus

class SendClientDiagnosticRemoteOperation(
private val syncConflict: Problem?,
private val problems: List<Problem>?,
private val virusDetected: Problem?,
private val e2eError: Problem?
) : RemoteOperation<Void>() {
override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
val request = RequestBody.create("application/json".toMediaTypeOrNull(), generateJSON())

val putMethod = PutMethod(client.baseUri.toString() + URL, true, request)

val status = putMethod.execute(client)

return if (status == HttpStatus.SC_OK) {
RemoteOperationResult<Void>(true, putMethod)
} else {
RemoteOperationResult<Void>(false, putMethod)
}
}

@VisibleForTesting
fun generateJSON(): String {
val gson = Gson()
// val map = HashMap<String, String?>()
val map = mutableListOf<String>()

if (syncConflict != null) {
map.add(syncConflict.toJsonWithTypeString())
} else {
map.add(""""$SYNC_CONFLICTS": {}""")
}

if (problems != null) {
val test = problems.map { it.toJsonWithTypeString() }
map.add(""""$PROBLEMS": ${test.joinToString(", ", "{", "}")}""")
} else {
map.add(""""$PROBLEMS": {}""")
}

if (virusDetected != null) {
map.add(virusDetected.toJsonWithTypeString())
} else {
map.add(""""$VIRUS_DETECTED": {}""")
}

if (e2eError != null) {
map.add(e2eError.toJsonWithTypeString())
} else {
map.add(""""$E2E_ERRORS": {}""")
}

// return gson.toJson(map)
return map.joinToString(prefix = "{", postfix = "}")
}

companion object {
const val URL = "/ocs/v2.php/apps/security_guard/diagnostics"

const val SYNC_CONFLICTS = "sync_conflicts"
const val PROBLEMS = "problems"
const val VIRUS_DETECTED = "virus_detected"
const val E2E_ERRORS = "e2e_errors"
}
}

0 comments on commit ec4a43e

Please sign in to comment.