Skip to content

Commit ec4a43e

Browse files
Add client reporting
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
1 parent cf3f1ae commit ec4a43e

File tree

9 files changed

+259
-11
lines changed

9 files changed

+259
-11
lines changed

library/src/androidTest/java/com/owncloud/android/GetCapabilitiesIT.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@
2626
*/
2727
package com.owncloud.android;
2828

29-
import static org.junit.Assert.assertEquals;
30-
import static org.junit.Assert.assertFalse;
31-
import static org.junit.Assert.assertNotNull;
32-
import static org.junit.Assert.assertSame;
33-
import static org.junit.Assert.assertTrue;
34-
3529
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
3630
import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
3731
import com.owncloud.android.lib.resources.status.GetCapabilitiesRemoteOperation;
@@ -41,6 +35,12 @@
4135

4236
import org.junit.Test;
4337

38+
import static org.junit.Assert.assertEquals;
39+
import static org.junit.Assert.assertFalse;
40+
import static org.junit.Assert.assertNotNull;
41+
import static org.junit.Assert.assertSame;
42+
import static org.junit.Assert.assertTrue;
43+
4444
/**
4545
* Class to test GetRemoteCapabilitiesOperation
4646
*/
@@ -150,12 +150,17 @@ private void checkCapability(OCCapability capability, String userId) {
150150
// groupfolder
151151
if (capability.getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_27)) {
152152
if (userId.equals("test")) {
153-
capability.getGroupfolders().isTrue();
153+
assertTrue(capability.getGroupfolders().isTrue());
154154
} else {
155-
capability.getGroupfolders().isFalse();
155+
assertTrue(capability.getGroupfolders().isFalse());
156156
}
157157
} else {
158-
capability.getGroupfolders().isFalse();
158+
assertTrue(capability.getGroupfolders().isFalse());
159+
}
160+
161+
// security guard
162+
if (capability.getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_28)) {
163+
assertTrue(capability.getSecurityGuard().isFalse());
159164
}
160165
}
161166
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Nextcloud Android client application
3+
*
4+
* @author Tobias Kaminsky
5+
* Copyright (C) 2023 Tobias Kaminsky
6+
* Copyright (C) 2023 Nextcloud GmbH
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
package com.owncloud.android.lib.resources.status
24+
25+
import com.owncloud.android.AbstractIT
26+
import org.junit.Assert.assertEquals
27+
import org.junit.Assert.assertTrue
28+
import org.junit.Test
29+
30+
@Suppress("Detekt.MagicNumber")
31+
class SendClientDiagnosticRemoteOperationIT : AbstractIT() {
32+
@Test
33+
@Suppress("Detekt.MaxLineLength", "ktlint:standard:max-line-length")
34+
fun testJSON() {
35+
val problems =
36+
listOf(
37+
Problem("UploadResult.CREDENTIAL_ERROR", 2, 1700152062),
38+
Problem("UploadResult.FOLDER_ERROR", 3, 1400652062)
39+
)
40+
41+
val sut =
42+
SendClientDiagnosticRemoteOperation(
43+
Problem(SendClientDiagnosticRemoteOperation.SYNC_CONFLICTS, 1, 1700652062),
44+
problems,
45+
Problem(SendClientDiagnosticRemoteOperation.VIRUS_DETECTED, 4, 1234234234),
46+
Problem(SendClientDiagnosticRemoteOperation.E2E_ERRORS, 2, 1700612062)
47+
)
48+
assertEquals(
49+
"""{"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}}""",
50+
sut.generateJSON()
51+
)
52+
}
53+
54+
@Test
55+
fun testEmptyJSON() {
56+
val sut =
57+
SendClientDiagnosticRemoteOperation(
58+
null,
59+
null,
60+
null,
61+
null
62+
)
63+
assertEquals(
64+
"""{"sync_conflicts": {}, "problems": {}, "virus_detected": {}, "e2e_errors": {}}""",
65+
sut.generateJSON()
66+
)
67+
}
68+
69+
@Test
70+
fun sendDiagnostic() {
71+
val problems =
72+
listOf(
73+
Problem("UploadResult.CREDENTIAL_ERROR", 2, 1700152062),
74+
Problem("UploadResult.FOLDER_ERROR", 3, 1400652062)
75+
)
76+
77+
val sut =
78+
SendClientDiagnosticRemoteOperation(
79+
Problem(SendClientDiagnosticRemoteOperation.SYNC_CONFLICTS, 1, 1700652062),
80+
problems,
81+
null,
82+
Problem(SendClientDiagnosticRemoteOperation.E2E_ERRORS, 2, 1700612062)
83+
).execute(nextcloudClient)
84+
assertTrue(sut.isSuccess) // we cannot check anything else
85+
}
86+
}

library/src/main/java/com/owncloud/android/lib/common/OwnCloudAccount.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public OwnCloudAccount(Account savedAccount, Context context) throws AccountNotF
7272
name = savedAccount.name;
7373
credentials = null; // load of credentials is delayed
7474

75-
AccountManager ama = AccountManager.get(context.getApplicationContext());
75+
AccountManager ama = AccountManager.get(context);
7676
String baseUrl = ama.getUserData(this.savedAccount, AccountUtils.Constants.KEY_OC_BASE_URL);
7777
if (baseUrl == null) {
7878
throw new AccountNotFoundException(this.savedAccount, "Account not found", null);

library/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static String constructBasicURLForAccount(Context context, Account accoun
7878
*/
7979
public static String getBaseUrlForAccount(Context context, Account account)
8080
throws AccountNotFoundException {
81-
AccountManager ama = AccountManager.get(context.getApplicationContext());
81+
AccountManager ama = AccountManager.get(context);
8282
String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
8383

8484
if (baseurl == null)

library/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
import javax.net.ssl.SSLException;
6262

63+
import androidx.annotation.Nullable;
6364
import okhttp3.Headers;
6465

6566

@@ -548,6 +549,7 @@ public ResultCode getCode() {
548549
return mCode;
549550
}
550551

552+
@Nullable
551553
public Exception getException() {
552554
return mException;
553555
}

library/src/main/java/com/owncloud/android/lib/resources/status/GetCapabilitiesRemoteOperation.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ public class GetCapabilitiesRemoteOperation extends RemoteOperation {
169169
// drop-account
170170
private static final String NODE_DROP_ACCOUNT = "drop-account";
171171

172+
// security guard
173+
private static final String NODE_SECURITY_GUARD = "security_guard";
174+
private static final String NODE_DIAGNOSTICS = "diagnostics";
172175

173176
private OCCapability currentCapability = null;
174177

@@ -687,6 +690,19 @@ private OCCapability parseResponse(String response) throws JSONException {
687690
capability.setDropAccount(CapabilityBooleanType.FALSE);
688691
}
689692
}
693+
694+
// security guard
695+
if (respCapabilities.has(NODE_SECURITY_GUARD)) {
696+
JSONObject securityGuardCapability = respCapabilities.getJSONObject(NODE_SECURITY_GUARD);
697+
698+
if (securityGuardCapability.getBoolean(NODE_DIAGNOSTICS)) {
699+
capability.setSecurityGuard(CapabilityBooleanType.TRUE);
700+
} else {
701+
capability.setSecurityGuard(CapabilityBooleanType.FALSE);
702+
}
703+
} else {
704+
capability.setSecurityGuard(CapabilityBooleanType.FALSE);
705+
}
690706
}
691707

692708
Log_OC.d(TAG, "*** Get Capabilities completed ");

library/src/main/java/com/owncloud/android/lib/resources/status/OCCapability.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class OCCapability {
113113
// Drop-Account
114114
var dropAccount = CapabilityBooleanType.UNKNOWN
115115

116+
// Security guard
117+
var securityGuard = CapabilityBooleanType.UNKNOWN
118+
116119
// Etag for capabilities
117120
var etag: String? = ""
118121

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Nextcloud Android client application
3+
*
4+
* @author Tobias Kaminsky
5+
* Copyright (C) 2023 Tobias Kaminsky
6+
* Copyright (C) 2023 Nextcloud GmbH
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
package com.owncloud.android.lib.resources.status
24+
25+
data class Problem(val type: String, val count: Int, val oldestTimestamp: Long) {
26+
fun toJsonString(): String {
27+
return """{"count": $count, "oldest": $oldestTimestamp}"""
28+
}
29+
30+
fun toJsonWithTypeString(): String {
31+
return """"$type": {"count": $count, "oldest": $oldestTimestamp}"""
32+
}
33+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* Nextcloud Android Library is available under MIT license
2+
*
3+
* @author Tobias Kaminsky
4+
* Copyright (C) 2023 Tobias Kaminsky
5+
* Copyright (C) 2023 Nextcloud GmbH
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*
26+
*/
27+
28+
package com.owncloud.android.lib.resources.status
29+
30+
import androidx.annotation.VisibleForTesting
31+
import com.google.gson.Gson
32+
import com.nextcloud.common.NextcloudClient
33+
import com.nextcloud.operations.PutMethod
34+
import com.owncloud.android.lib.common.operations.RemoteOperation
35+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
36+
import okhttp3.MediaType.Companion.toMediaTypeOrNull
37+
import okhttp3.RequestBody
38+
import org.apache.commons.httpclient.HttpStatus
39+
40+
class SendClientDiagnosticRemoteOperation(
41+
private val syncConflict: Problem?,
42+
private val problems: List<Problem>?,
43+
private val virusDetected: Problem?,
44+
private val e2eError: Problem?
45+
) : RemoteOperation<Void>() {
46+
override fun run(client: NextcloudClient): RemoteOperationResult<Void> {
47+
val request = RequestBody.create("application/json".toMediaTypeOrNull(), generateJSON())
48+
49+
val putMethod = PutMethod(client.baseUri.toString() + URL, true, request)
50+
51+
val status = putMethod.execute(client)
52+
53+
return if (status == HttpStatus.SC_OK) {
54+
RemoteOperationResult<Void>(true, putMethod)
55+
} else {
56+
RemoteOperationResult<Void>(false, putMethod)
57+
}
58+
}
59+
60+
@VisibleForTesting
61+
fun generateJSON(): String {
62+
val gson = Gson()
63+
// val map = HashMap<String, String?>()
64+
val map = mutableListOf<String>()
65+
66+
if (syncConflict != null) {
67+
map.add(syncConflict.toJsonWithTypeString())
68+
} else {
69+
map.add(""""$SYNC_CONFLICTS": {}""")
70+
}
71+
72+
if (problems != null) {
73+
val test = problems.map { it.toJsonWithTypeString() }
74+
map.add(""""$PROBLEMS": ${test.joinToString(", ", "{", "}")}""")
75+
} else {
76+
map.add(""""$PROBLEMS": {}""")
77+
}
78+
79+
if (virusDetected != null) {
80+
map.add(virusDetected.toJsonWithTypeString())
81+
} else {
82+
map.add(""""$VIRUS_DETECTED": {}""")
83+
}
84+
85+
if (e2eError != null) {
86+
map.add(e2eError.toJsonWithTypeString())
87+
} else {
88+
map.add(""""$E2E_ERRORS": {}""")
89+
}
90+
91+
// return gson.toJson(map)
92+
return map.joinToString(prefix = "{", postfix = "}")
93+
}
94+
95+
companion object {
96+
const val URL = "/ocs/v2.php/apps/security_guard/diagnostics"
97+
98+
const val SYNC_CONFLICTS = "sync_conflicts"
99+
const val PROBLEMS = "problems"
100+
const val VIRUS_DETECTED = "virus_detected"
101+
const val E2E_ERRORS = "e2e_errors"
102+
}
103+
}

0 commit comments

Comments
 (0)