From 3688e87ee4a97030d06b0fc44e2f601ffc271bbd Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 12 Nov 2026 11:28:03 +0100 Subject: [PATCH 1/7] add endpoints Signed-off-by: alperozturk96 --- .../folder/GetTeamFolderRemoteOperationIT.kt | 55 ++++++++++++++++ .../GetTeamMembersRemoteOperationIT.kt | 45 +++++++++++++ .../GetTeamResourcesRemoteOperationIT.kt | 40 ++++++++++++ .../get/team/GetTeamsRemoteOperationIT.kt | 46 +++++++++++++ .../AddTeamMembersRemoteOperationIT.kt | 46 +++++++++++++ .../post/team/CreateTeamRemoteOperationIT.kt | 41 ++++++++++++ .../folder/GetTeamFolderRemoteOperation.kt | 61 +++++++++++++++++ .../teams/get/folder/model/TeamFolder.kt | 17 +++++ .../members/GetTeamMembersRemoteOperation.kt | 61 +++++++++++++++++ .../GetTeamResourcesRemoteOperation.kt | 62 ++++++++++++++++++ .../teams/get/resources/model/TeamResource.kt | 21 ++++++ .../resources/model/TeamResourceProvider.kt | 17 +++++ .../resources/model/TeamResourcesResponse.kt | 15 +++++ .../teams/get/team/GetTeamsRemoteOperation.kt | 53 +++++++++++++++ .../android/lib/resources/teams/model/Team.kt | 30 +++++++++ .../lib/resources/teams/model/TeamMember.kt | 33 ++++++++++ .../resources/teams/model/TeamMemberType.kt | 39 +++++++++++ .../lib/resources/teams/model/TeamRole.kt | 29 +++++++++ .../members/AddTeamMembersRemoteOperation.kt | 65 +++++++++++++++++++ .../members/model/AddTeamMemberRequest.kt | 16 +++++ .../members/model/AddTeamMembersRequest.kt | 15 +++++ .../teams/post/members/model/NewTeamMember.kt | 15 +++++ .../post/team/CreateTeamRemoteOperation.kt | 63 ++++++++++++++++++ .../post/team/model/CreateTeamRequest.kt | 16 +++++ 24 files changed, 901 insertions(+) create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperationIT.kt create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperationIT.kt create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperationIT.kt create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperationIT.kt create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperationIT.kt create mode 100644 library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperationIT.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/model/TeamFolder.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResource.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourceProvider.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourcesResponse.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/model/Team.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMember.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMemberType.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamRole.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMemberRequest.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMembersRequest.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/NewTeamMember.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt create mode 100644 library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/model/CreateTeamRequest.kt diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperationIT.kt new file mode 100644 index 0000000000..017c6ea49a --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperationIT.kt @@ -0,0 +1,55 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.folder + +import com.nextcloud.android.lib.resources.teams.post.team.CreateTeamRemoteOperation +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.apache.commons.httpclient.HttpStatus +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class GetTeamFolderRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun getTeamFolder() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), true) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + + val result = GetTeamFolderRemoteOperation(created.resultData.id).execute(nextcloudClient) + + assertTrue(result.isSuccess) + assertTrue(result.resultData.mountPoint.isNotEmpty()) + } + + @Test + fun getTeamFolderWithoutFolder() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), false) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + + val result = GetTeamFolderRemoteOperation(created.resultData.id).execute(nextcloudClient) + + assertFalse(result.isSuccess) + assertEquals(HttpStatus.SC_NOT_FOUND, result.httpCode) + } + + companion object { + private const val TEAM_NAME = "Team " + } +} diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperationIT.kt new file mode 100644 index 0000000000..ad4bb29604 --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperationIT.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.members + +import com.nextcloud.android.lib.resources.teams.model.TeamMemberType +import com.nextcloud.android.lib.resources.teams.model.TeamRole +import com.nextcloud.android.lib.resources.teams.post.team.CreateTeamRemoteOperation +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class GetTeamMembersRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun getMembers() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), false) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + + val result = GetTeamMembersRemoteOperation(created.resultData.id).execute(nextcloudClient) + + assertTrue(result.isSuccess) + assertTrue( + result.resultData.any { + it.userId == nextcloudClient.userId && it.role == TeamRole.OWNER && it.type == TeamMemberType.USER + } + ) + } + + companion object { + private const val TEAM_NAME = "Team " + } +} diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperationIT.kt new file mode 100644 index 0000000000..a00c6363dd --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperationIT.kt @@ -0,0 +1,40 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.resources + +import com.nextcloud.android.lib.resources.teams.post.team.CreateTeamRemoteOperation +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class GetTeamResourcesRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun getResources() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), false) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + + val result = GetTeamResourcesRemoteOperation(created.resultData.id).execute(nextcloudClient) + + assertTrue(result.isSuccess) + assertNotNull(result.resultData) + } + + companion object { + private const val TEAM_NAME = "Team " + } +} diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperationIT.kt new file mode 100644 index 0000000000..57123b9e45 --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperationIT.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.team + +import com.nextcloud.android.lib.resources.teams.model.TeamRole +import com.nextcloud.android.lib.resources.teams.post.team.CreateTeamRemoteOperation +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class GetTeamsRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun getTeams() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), false) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + + val result = GetTeamsRemoteOperation().execute(nextcloudClient) + + assertTrue(result.isSuccess) + val team = result.resultData.firstOrNull { it.id == created.resultData.id } + assertNotNull(team) + assertTrue(team!!.isMember) + assertEquals(TeamRole.OWNER, team.role) + assertEquals(created.resultData.url, team.url) + } + + companion object { + private const val TEAM_NAME = "Team " + } +} diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperationIT.kt new file mode 100644 index 0000000000..55877413c7 --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperationIT.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.members + +import com.nextcloud.android.lib.resources.teams.get.members.GetTeamMembersRemoteOperation +import com.nextcloud.android.lib.resources.teams.model.TeamMemberType +import com.nextcloud.android.lib.resources.teams.post.members.model.NewTeamMember +import com.nextcloud.android.lib.resources.teams.post.team.CreateTeamRemoteOperation +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class AddTeamMembersRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun addMembers() { + val created = + CreateTeamRemoteOperation(TEAM_NAME + System.currentTimeMillis(), false) + .execute(nextcloudClient) + assertTrue(created.isSuccess) + val teamId = created.resultData.id + val members = listOf(NewTeamMember(MEMBER_USER_ID, TeamMemberType.USER)) + + val result = AddTeamMembersRemoteOperation(teamId, members).execute(nextcloudClient) + + assertTrue(result.isSuccess) + val teamMembers = GetTeamMembersRemoteOperation(teamId).execute(nextcloudClient) + assertTrue(teamMembers.resultData.any { it.userId == MEMBER_USER_ID }) + } + + companion object { + private const val TEAM_NAME = "Team " + private const val MEMBER_USER_ID = "user1" + } +} diff --git a/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperationIT.kt b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperationIT.kt new file mode 100644 index 0000000000..f623129511 --- /dev/null +++ b/library/src/androidTest/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperationIT.kt @@ -0,0 +1,41 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.team + +import com.nextcloud.android.lib.resources.teams.model.TeamRole +import com.owncloud.android.AbstractIT +import com.owncloud.android.lib.resources.status.NextcloudVersion +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class CreateTeamRemoteOperationIT : AbstractIT() { + @Before + fun before() { + testOnlyOnServer(NextcloudVersion.nextcloud_34) + } + + @Test + fun createTeam() { + val name = TEAM_NAME + System.currentTimeMillis() + + val result = CreateTeamRemoteOperation(name, false).execute(nextcloudClient) + + assertTrue(result.isSuccess) + val team = result.resultData + assertTrue(team.id.isNotEmpty()) + assertEquals(name, team.displayName) + assertTrue(team.url.isNotEmpty()) + assertEquals(TeamRole.OWNER, team.role) + } + + companion object { + private const val TEAM_NAME = "Team " + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt new file mode 100644 index 0000000000..2c74a95d0c --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt @@ -0,0 +1,61 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.folder + +import com.nextcloud.android.lib.resources.teams.get.folder.model.TeamFolder +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.SEPARATOR +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import org.apache.commons.httpclient.HttpStatus + +class GetTeamFolderRemoteOperation( + private val teamId: String +) : OCSRemoteOperation() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult { + val getMethod = + GetMethod( + client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + FOLDER + JSON_FORMAT, + true + ) + return try { + val status = client.execute(getMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Get team folder failed with status code: $status") + return RemoteOperationResult(false, getMethod) + } + val response = + ocsJson.decodeFromString>( + getMethod.getResponseBodyAsString() + ) + val data = response.ocs.data + RemoteOperationResult(true, getMethod).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + getMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Get team folder failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = GetTeamFolderRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/apps/circles/teams" + private const val FOLDER = "/folder" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/model/TeamFolder.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/model/TeamFolder.kt new file mode 100644 index 0000000000..bbd7ca600d --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/model/TeamFolder.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.folder.model + +import kotlinx.serialization.Serializable + +@Serializable +data class TeamFolder( + val id: Long, + val mountPoint: String, + val quota: Long? = null +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt new file mode 100644 index 0000000000..7fb869748e --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt @@ -0,0 +1,61 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.members + +import com.nextcloud.android.lib.resources.teams.model.TeamMember +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.SEPARATOR +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import org.apache.commons.httpclient.HttpStatus + +class GetTeamMembersRemoteOperation( + private val teamId: String +) : OCSRemoteOperation>() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult> { + val getMethod = + GetMethod( + client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + MEMBERS + JSON_FORMAT, + true + ) + return try { + val status = client.execute(getMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Get team members failed with status code: $status") + return RemoteOperationResult(false, getMethod) + } + val response = + ocsJson.decodeFromString>>( + getMethod.getResponseBodyAsString() + ) + val data = response.ocs.data + RemoteOperationResult>(true, getMethod).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + getMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult> = + RemoteOperationResult>(e).also { + Log_OC.e(TAG, "Get team members failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = GetTeamMembersRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" + private const val MEMBERS = "/members" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt new file mode 100644 index 0000000000..9f79177da5 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt @@ -0,0 +1,62 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.resources + +import com.nextcloud.android.lib.resources.teams.get.resources.model.TeamResource +import com.nextcloud.android.lib.resources.teams.get.resources.model.TeamResourcesResponse +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.SEPARATOR +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import org.apache.commons.httpclient.HttpStatus + +class GetTeamResourcesRemoteOperation( + private val teamId: String +) : OCSRemoteOperation>() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult> { + val getMethod = + GetMethod( + client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + RESOURCES + JSON_FORMAT, + true + ) + return try { + val status = client.execute(getMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Get team resources failed with status code: $status") + return RemoteOperationResult(false, getMethod) + } + val response = + ocsJson.decodeFromString>( + getMethod.getResponseBodyAsString() + ) + val data = response.ocs.data.resources + RemoteOperationResult>(true, getMethod).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + getMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult> = + RemoteOperationResult>(e).also { + Log_OC.e(TAG, "Get team resources failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = GetTeamResourcesRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/teams" + private const val RESOURCES = "/resources" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResource.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResource.kt new file mode 100644 index 0000000000..d17c1323e5 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResource.kt @@ -0,0 +1,21 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.resources.model + +import kotlinx.serialization.Serializable + +@Serializable +data class TeamResource( + val id: String, + val label: String, + val url: String, + val iconSvg: String? = null, + val iconURL: String? = null, + val iconEmoji: String? = null, + val provider: TeamResourceProvider +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourceProvider.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourceProvider.kt new file mode 100644 index 0000000000..9fab90b55d --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourceProvider.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.resources.model + +import kotlinx.serialization.Serializable + +@Serializable +data class TeamResourceProvider( + val id: String, + val name: String, + val icon: String? = null +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourcesResponse.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourcesResponse.kt new file mode 100644 index 0000000000..555b31685c --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/model/TeamResourcesResponse.kt @@ -0,0 +1,15 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.resources.model + +import kotlinx.serialization.Serializable + +@Serializable +internal data class TeamResourcesResponse( + val resources: List +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt new file mode 100644 index 0000000000..3d31061172 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.get.team + +import com.nextcloud.android.lib.resources.teams.model.Team +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.GetMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import org.apache.commons.httpclient.HttpStatus + +class GetTeamsRemoteOperation : OCSRemoteOperation>() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult> { + val getMethod = GetMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true) + return try { + val status = client.execute(getMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Get teams failed with status code: $status") + return RemoteOperationResult(false, getMethod) + } + val response = + ocsJson.decodeFromString>>( + getMethod.getResponseBodyAsString() + ) + val data = response.ocs.data + RemoteOperationResult>(true, getMethod).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + getMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult> = + RemoteOperationResult>(e).also { + Log_OC.e(TAG, "Get teams failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = GetTeamsRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/Team.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/Team.kt new file mode 100644 index 0000000000..8383ab914f --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/Team.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.model + +import kotlinx.serialization.Serializable + +@Serializable +data class Team( + val id: String, + val name: String, + val displayName: String, + val description: String = "", + val population: Int = 0, + val url: String = "", + val initiator: TeamMember? = null +) { + val isMember: Boolean + get() = initiator != null && !isPendingRequest + + val isPendingRequest: Boolean + get() = initiator?.isPendingRequest == true + + val role: TeamRole? + get() = initiator?.takeIf { isMember }?.role +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMember.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMember.kt new file mode 100644 index 0000000000..9d8e9b2321 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMember.kt @@ -0,0 +1,33 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.model + +import kotlinx.serialization.Serializable + +@Serializable +data class TeamMember( + val singleId: String, + val userId: String? = null, + val displayName: String = "", + val userType: Int = 0, + val level: Int = 0, + val status: String = "" +) { + val role: TeamRole + get() = TeamRole.fromLevel(level) + + val type: TeamMemberType? + get() = TeamMemberType.fromValue(userType) + + val isPendingRequest: Boolean + get() = status == STATUS_REQUESTING + + companion object { + const val STATUS_REQUESTING = "Requesting" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMemberType.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMemberType.kt new file mode 100644 index 0000000000..2d38068fc1 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamMemberType.kt @@ -0,0 +1,39 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.model + +import com.owncloud.android.lib.resources.shares.ShareType + +private const val TYPE_USER = 1 +private const val TYPE_GROUP = 2 +private const val TYPE_MAIL = 4 +private const val TYPE_CONTACT = 8 +private const val TYPE_CIRCLE = 16 + +enum class TeamMemberType( + val value: Int +) { + USER(TYPE_USER), + GROUP(TYPE_GROUP), + MAIL(TYPE_MAIL), + CONTACT(TYPE_CONTACT), + CIRCLE(TYPE_CIRCLE); + + companion object { + fun fromValue(value: Int): TeamMemberType? = entries.firstOrNull { it.value == value } + + fun fromShareType(shareType: ShareType): TeamMemberType? = + when (shareType) { + ShareType.USER, ShareType.FEDERATED -> USER + ShareType.GROUP -> GROUP + ShareType.EMAIL -> MAIL + ShareType.CIRCLE -> CIRCLE + else -> null + } + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamRole.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamRole.kt new file mode 100644 index 0000000000..7c9c6bb6af --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/model/TeamRole.kt @@ -0,0 +1,29 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.model + +private const val LEVEL_MODERATOR = 4 +private const val LEVEL_ADMIN = 8 +private const val LEVEL_OWNER = 9 + +enum class TeamRole { + OWNER, + ADMIN, + MODERATOR, + MEMBER; + + companion object { + fun fromLevel(level: Int): TeamRole = + when { + level >= LEVEL_OWNER -> OWNER + level >= LEVEL_ADMIN -> ADMIN + level >= LEVEL_MODERATOR -> MODERATOR + else -> MEMBER + } + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt new file mode 100644 index 0000000000..0c083e1a0f --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt @@ -0,0 +1,65 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.members + +import com.nextcloud.android.lib.resources.teams.post.members.model.AddTeamMemberRequest +import com.nextcloud.android.lib.resources.teams.post.members.model.AddTeamMembersRequest +import com.nextcloud.android.lib.resources.teams.post.members.model.NewTeamMember +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.PostMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.SEPARATOR +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import kotlinx.serialization.encodeToString +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody +import org.apache.commons.httpclient.HttpStatus + +class AddTeamMembersRemoteOperation( + private val teamId: String, + private val members: List +) : OCSRemoteOperation() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult { + val request = AddTeamMembersRequest(members.map { AddTeamMemberRequest(it.id, it.type.value) }) + val body = ocsJson.encodeToString(request).toRequestBody(JSON_MEDIA_TYPE.toMediaTypeOrNull()) + val postMethod = + PostMethod( + client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + MEMBERS_MULTI + JSON_FORMAT, + true, + body + ) + return try { + val status = client.execute(postMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Add team members failed with status code: $status") + return RemoteOperationResult(false, postMethod) + } + RemoteOperationResult(true, postMethod) + } catch (e: Exception) { + failure(e) + } finally { + postMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Add team members failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = AddTeamMembersRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" + private const val MEMBERS_MULTI = "/members/multi" + private const val JSON_MEDIA_TYPE = "application/json" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMemberRequest.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMemberRequest.kt new file mode 100644 index 0000000000..9893226154 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMemberRequest.kt @@ -0,0 +1,16 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.members.model + +import kotlinx.serialization.Serializable + +@Serializable +internal data class AddTeamMemberRequest( + val id: String, + val type: Int +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMembersRequest.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMembersRequest.kt new file mode 100644 index 0000000000..327894f426 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/AddTeamMembersRequest.kt @@ -0,0 +1,15 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.members.model + +import kotlinx.serialization.Serializable + +@Serializable +internal data class AddTeamMembersRequest( + val members: List +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/NewTeamMember.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/NewTeamMember.kt new file mode 100644 index 0000000000..8103c52440 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/model/NewTeamMember.kt @@ -0,0 +1,15 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.members.model + +import com.nextcloud.android.lib.resources.teams.model.TeamMemberType + +data class NewTeamMember( + val id: String, + val type: TeamMemberType +) diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt new file mode 100644 index 0000000000..600bf9c341 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt @@ -0,0 +1,63 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.team + +import com.nextcloud.android.lib.resources.teams.post.team.model.CreateTeamRequest +import com.nextcloud.android.lib.resources.teams.model.Team +import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.PostMethod +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.ocsJson +import com.owncloud.android.lib.resources.OCSRemoteOperation +import kotlinx.serialization.encodeToString +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody +import org.apache.commons.httpclient.HttpStatus + +class CreateTeamRemoteOperation( + private val name: String, + private val createTeamFolder: Boolean = true +) : OCSRemoteOperation() { + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult { + val request = CreateTeamRequest(name, createTeamFolder) + val body = ocsJson.encodeToString(request).toRequestBody(JSON_MEDIA_TYPE.toMediaTypeOrNull()) + val postMethod = PostMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true, body) + return try { + val status = client.execute(postMethod) + if (status != HttpStatus.SC_OK) { + Log_OC.e(TAG, "Create team failed with status code: $status") + return RemoteOperationResult(false, postMethod) + } + val response = + ocsJson.decodeFromString>( + postMethod.getResponseBodyAsString() + ) + val data = response.ocs.data + RemoteOperationResult(true, postMethod).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + postMethod.releaseConnection() + } + } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Create team failed: " + it.logMessage, it.exception) + } + + companion object { + private val TAG = CreateTeamRemoteOperation::class.java.simpleName + private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" + private const val JSON_MEDIA_TYPE = "application/json" + } +} diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/model/CreateTeamRequest.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/model/CreateTeamRequest.kt new file mode 100644 index 0000000000..8d2de57309 --- /dev/null +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/model/CreateTeamRequest.kt @@ -0,0 +1,16 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.android.lib.resources.teams.post.team.model + +import kotlinx.serialization.Serializable + +@Serializable +internal data class CreateTeamRequest( + val name: String, + val createTeamFolder: Boolean +) From 6ab16b2022034a8b7f5ead0b855f7ae428d5a986 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 12 Nov 2026 11:32:27 +0100 Subject: [PATCH 2/7] de-duplicate call logic Signed-off-by: alperozturk96 --- .../folder/GetTeamFolderRemoteOperation.kt | 44 ++---------- .../members/GetTeamMembersRemoteOperation.kt | 45 ++---------- .../GetTeamResourcesRemoteOperation.kt | 44 ++---------- .../teams/get/team/GetTeamsRemoteOperation.kt | 40 ++--------- .../members/AddTeamMembersRemoteOperation.kt | 50 +++----------- .../post/team/CreateTeamRemoteOperation.kt | 52 +++----------- .../operations/OcsKotlinRemoteOperation.kt | 68 +++++++++++++++++++ 7 files changed, 110 insertions(+), 233 deletions(-) create mode 100644 library/src/main/java/com/nextcloud/operations/OcsKotlinRemoteOperation.kt diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt index 2c74a95d0c..00b0f3ec8b 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/folder/GetTeamFolderRemoteOperation.kt @@ -8,53 +8,19 @@ package com.nextcloud.android.lib.resources.teams.get.folder import com.nextcloud.android.lib.resources.teams.get.folder.model.TeamFolder -import com.nextcloud.common.NextcloudClient import com.nextcloud.operations.GetMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.nextcloud.operations.OcsKotlinRemoteOperation import com.owncloud.android.lib.ocs.SEPARATOR -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import org.apache.commons.httpclient.HttpStatus class GetTeamFolderRemoteOperation( private val teamId: String -) : OCSRemoteOperation() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult { - val getMethod = - GetMethod( - client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + FOLDER + JSON_FORMAT, - true - ) - return try { - val status = client.execute(getMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Get team folder failed with status code: $status") - return RemoteOperationResult(false, getMethod) - } - val response = - ocsJson.decodeFromString>( - getMethod.getResponseBodyAsString() - ) - val data = response.ocs.data - RemoteOperationResult(true, getMethod).apply { resultData = data } - } catch (e: Exception) { - failure(e) - } finally { - getMethod.releaseConnection() - } - } +) : OcsKotlinRemoteOperation() { + override fun createMethod(baseUrl: String) = + GetMethod(baseUrl + ENDPOINT + SEPARATOR + teamId + FOLDER + JSON_FORMAT, true) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult = - RemoteOperationResult(e).also { - Log_OC.e(TAG, "Get team folder failed: " + it.logMessage, it.exception) - } + override fun parse(body: String) = decode(body, TeamFolder.serializer()) companion object { - private val TAG = GetTeamFolderRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/apps/circles/teams" private const val FOLDER = "/folder" } diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt index 7fb869748e..a4306bfacd 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/members/GetTeamMembersRemoteOperation.kt @@ -8,53 +8,20 @@ package com.nextcloud.android.lib.resources.teams.get.members import com.nextcloud.android.lib.resources.teams.model.TeamMember -import com.nextcloud.common.NextcloudClient import com.nextcloud.operations.GetMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.nextcloud.operations.OcsKotlinRemoteOperation import com.owncloud.android.lib.ocs.SEPARATOR -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import org.apache.commons.httpclient.HttpStatus +import kotlinx.serialization.builtins.ListSerializer class GetTeamMembersRemoteOperation( private val teamId: String -) : OCSRemoteOperation>() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult> { - val getMethod = - GetMethod( - client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + MEMBERS + JSON_FORMAT, - true - ) - return try { - val status = client.execute(getMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Get team members failed with status code: $status") - return RemoteOperationResult(false, getMethod) - } - val response = - ocsJson.decodeFromString>>( - getMethod.getResponseBodyAsString() - ) - val data = response.ocs.data - RemoteOperationResult>(true, getMethod).apply { resultData = data } - } catch (e: Exception) { - failure(e) - } finally { - getMethod.releaseConnection() - } - } +) : OcsKotlinRemoteOperation>() { + override fun createMethod(baseUrl: String) = + GetMethod(baseUrl + ENDPOINT + SEPARATOR + teamId + MEMBERS + JSON_FORMAT, true) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult> = - RemoteOperationResult>(e).also { - Log_OC.e(TAG, "Get team members failed: " + it.logMessage, it.exception) - } + override fun parse(body: String) = decode(body, ListSerializer(TeamMember.serializer())) companion object { - private val TAG = GetTeamMembersRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" private const val MEMBERS = "/members" } diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt index 9f79177da5..81181a0516 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/resources/GetTeamResourcesRemoteOperation.kt @@ -9,53 +9,19 @@ package com.nextcloud.android.lib.resources.teams.get.resources import com.nextcloud.android.lib.resources.teams.get.resources.model.TeamResource import com.nextcloud.android.lib.resources.teams.get.resources.model.TeamResourcesResponse -import com.nextcloud.common.NextcloudClient import com.nextcloud.operations.GetMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.nextcloud.operations.OcsKotlinRemoteOperation import com.owncloud.android.lib.ocs.SEPARATOR -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import org.apache.commons.httpclient.HttpStatus class GetTeamResourcesRemoteOperation( private val teamId: String -) : OCSRemoteOperation>() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult> { - val getMethod = - GetMethod( - client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + RESOURCES + JSON_FORMAT, - true - ) - return try { - val status = client.execute(getMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Get team resources failed with status code: $status") - return RemoteOperationResult(false, getMethod) - } - val response = - ocsJson.decodeFromString>( - getMethod.getResponseBodyAsString() - ) - val data = response.ocs.data.resources - RemoteOperationResult>(true, getMethod).apply { resultData = data } - } catch (e: Exception) { - failure(e) - } finally { - getMethod.releaseConnection() - } - } +) : OcsKotlinRemoteOperation>() { + override fun createMethod(baseUrl: String) = + GetMethod(baseUrl + ENDPOINT + SEPARATOR + teamId + RESOURCES + JSON_FORMAT, true) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult> = - RemoteOperationResult>(e).also { - Log_OC.e(TAG, "Get team resources failed: " + it.logMessage, it.exception) - } + override fun parse(body: String) = decode(body, TeamResourcesResponse.serializer()).resources companion object { - private val TAG = GetTeamResourcesRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/teams" private const val RESOURCES = "/resources" } diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt index 3d31061172..9e418b6bfe 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/get/team/GetTeamsRemoteOperation.kt @@ -8,46 +8,16 @@ package com.nextcloud.android.lib.resources.teams.get.team import com.nextcloud.android.lib.resources.teams.model.Team -import com.nextcloud.common.NextcloudClient import com.nextcloud.operations.GetMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.ocs.OcsKotlinResponse -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import org.apache.commons.httpclient.HttpStatus +import com.nextcloud.operations.OcsKotlinRemoteOperation +import kotlinx.serialization.builtins.ListSerializer -class GetTeamsRemoteOperation : OCSRemoteOperation>() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult> { - val getMethod = GetMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true) - return try { - val status = client.execute(getMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Get teams failed with status code: $status") - return RemoteOperationResult(false, getMethod) - } - val response = - ocsJson.decodeFromString>>( - getMethod.getResponseBodyAsString() - ) - val data = response.ocs.data - RemoteOperationResult>(true, getMethod).apply { resultData = data } - } catch (e: Exception) { - failure(e) - } finally { - getMethod.releaseConnection() - } - } +class GetTeamsRemoteOperation : OcsKotlinRemoteOperation>() { + override fun createMethod(baseUrl: String) = GetMethod(baseUrl + ENDPOINT + JSON_FORMAT, true) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult> = - RemoteOperationResult>(e).also { - Log_OC.e(TAG, "Get teams failed: " + it.logMessage, it.exception) - } + override fun parse(body: String) = decode(body, ListSerializer(Team.serializer())) companion object { - private val TAG = GetTeamsRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" } } diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt index 0c083e1a0f..a47d2db439 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/members/AddTeamMembersRemoteOperation.kt @@ -10,56 +10,28 @@ package com.nextcloud.android.lib.resources.teams.post.members import com.nextcloud.android.lib.resources.teams.post.members.model.AddTeamMemberRequest import com.nextcloud.android.lib.resources.teams.post.members.model.AddTeamMembersRequest import com.nextcloud.android.lib.resources.teams.post.members.model.NewTeamMember -import com.nextcloud.common.NextcloudClient +import com.nextcloud.operations.OcsKotlinRemoteOperation import com.nextcloud.operations.PostMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.ocs.SEPARATOR -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import kotlinx.serialization.encodeToString -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody -import org.apache.commons.httpclient.HttpStatus class AddTeamMembersRemoteOperation( private val teamId: String, private val members: List -) : OCSRemoteOperation() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult { - val request = AddTeamMembersRequest(members.map { AddTeamMemberRequest(it.id, it.type.value) }) - val body = ocsJson.encodeToString(request).toRequestBody(JSON_MEDIA_TYPE.toMediaTypeOrNull()) - val postMethod = - PostMethod( - client.baseUri.toString() + ENDPOINT + SEPARATOR + teamId + MEMBERS_MULTI + JSON_FORMAT, - true, - body +) : OcsKotlinRemoteOperation() { + override fun createMethod(baseUrl: String) = + PostMethod( + baseUrl + ENDPOINT + SEPARATOR + teamId + MEMBERS_MULTI + JSON_FORMAT, + true, + jsonBody( + AddTeamMembersRequest(members.map { AddTeamMemberRequest(it.id, it.type.value) }), + AddTeamMembersRequest.serializer() ) - return try { - val status = client.execute(postMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Add team members failed with status code: $status") - return RemoteOperationResult(false, postMethod) - } - RemoteOperationResult(true, postMethod) - } catch (e: Exception) { - failure(e) - } finally { - postMethod.releaseConnection() - } - } + ) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult = - RemoteOperationResult(e).also { - Log_OC.e(TAG, "Add team members failed: " + it.logMessage, it.exception) - } + override fun parse(body: String): Void? = null companion object { - private val TAG = AddTeamMembersRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" private const val MEMBERS_MULTI = "/members/multi" - private const val JSON_MEDIA_TYPE = "application/json" } } diff --git a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt index 600bf9c341..0de498dafc 100644 --- a/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt +++ b/library/src/main/java/com/nextcloud/android/lib/resources/teams/post/team/CreateTeamRemoteOperation.kt @@ -7,57 +7,25 @@ package com.nextcloud.android.lib.resources.teams.post.team -import com.nextcloud.android.lib.resources.teams.post.team.model.CreateTeamRequest import com.nextcloud.android.lib.resources.teams.model.Team -import com.nextcloud.common.NextcloudClient +import com.nextcloud.android.lib.resources.teams.post.team.model.CreateTeamRequest +import com.nextcloud.operations.OcsKotlinRemoteOperation import com.nextcloud.operations.PostMethod -import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.lib.ocs.OcsKotlinResponse -import com.owncloud.android.lib.ocs.ocsJson -import com.owncloud.android.lib.resources.OCSRemoteOperation -import kotlinx.serialization.encodeToString -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody -import org.apache.commons.httpclient.HttpStatus class CreateTeamRemoteOperation( private val name: String, private val createTeamFolder: Boolean = true -) : OCSRemoteOperation() { - @Suppress("TooGenericExceptionCaught") - override fun run(client: NextcloudClient): RemoteOperationResult { - val request = CreateTeamRequest(name, createTeamFolder) - val body = ocsJson.encodeToString(request).toRequestBody(JSON_MEDIA_TYPE.toMediaTypeOrNull()) - val postMethod = PostMethod(client.baseUri.toString() + ENDPOINT + JSON_FORMAT, true, body) - return try { - val status = client.execute(postMethod) - if (status != HttpStatus.SC_OK) { - Log_OC.e(TAG, "Create team failed with status code: $status") - return RemoteOperationResult(false, postMethod) - } - val response = - ocsJson.decodeFromString>( - postMethod.getResponseBodyAsString() - ) - val data = response.ocs.data - RemoteOperationResult(true, postMethod).apply { resultData = data } - } catch (e: Exception) { - failure(e) - } finally { - postMethod.releaseConnection() - } - } +) : OcsKotlinRemoteOperation() { + override fun createMethod(baseUrl: String) = + PostMethod( + baseUrl + ENDPOINT + JSON_FORMAT, + true, + jsonBody(CreateTeamRequest(name, createTeamFolder), CreateTeamRequest.serializer()) + ) - @Suppress("DEPRECATION") - private fun failure(e: Exception): RemoteOperationResult = - RemoteOperationResult(e).also { - Log_OC.e(TAG, "Create team failed: " + it.logMessage, it.exception) - } + override fun parse(body: String) = decode(body, Team.serializer()) companion object { - private val TAG = CreateTeamRemoteOperation::class.java.simpleName private const val ENDPOINT = "/ocs/v2.php/apps/circles/circles" - private const val JSON_MEDIA_TYPE = "application/json" } } diff --git a/library/src/main/java/com/nextcloud/operations/OcsKotlinRemoteOperation.kt b/library/src/main/java/com/nextcloud/operations/OcsKotlinRemoteOperation.kt new file mode 100644 index 0000000000..0bcb291cc5 --- /dev/null +++ b/library/src/main/java/com/nextcloud/operations/OcsKotlinRemoteOperation.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.nextcloud.operations + +import com.nextcloud.common.NextcloudClient +import com.nextcloud.common.OkHttpMethodBase +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.ocs.OcsKotlinResponse +import com.owncloud.android.lib.ocs.ocsJson +import kotlinx.serialization.KSerializer +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import org.apache.commons.httpclient.HttpStatus + +abstract class OcsKotlinRemoteOperation : RemoteOperation() { + protected abstract fun createMethod(baseUrl: String): OkHttpMethodBase + + protected abstract fun parse(body: String): T? + + @Suppress("TooGenericExceptionCaught") + override fun run(client: NextcloudClient): RemoteOperationResult { + val method = createMethod(client.baseUri.toString()) + return try { + val status = client.execute(method) + if (status != HttpStatus.SC_OK) { + Log_OC.e(tag, "Failed with status code: $status") + return RemoteOperationResult(false, method) + } + val data = parse(method.getResponseBodyAsString()) + RemoteOperationResult(true, method).apply { resultData = data } + } catch (e: Exception) { + failure(e) + } finally { + method.releaseConnection() + } + } + + protected fun decode( + body: String, + serializer: KSerializer + ): R = ocsJson.decodeFromString(OcsKotlinResponse.serializer(serializer), body).ocs.data + + protected fun jsonBody( + value: R, + serializer: KSerializer + ): RequestBody = ocsJson.encodeToString(serializer, value).toRequestBody(JSON_MEDIA_TYPE.toMediaTypeOrNull()) + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(tag, "Failed: " + it.logMessage, it.exception) + } + + private val tag: String + get() = javaClass.simpleName + + companion object { + private const val JSON_MEDIA_TYPE = "application/json" + } +} From af94a728e66d428693a247d4fdb8b072a14d8bf5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 12 Nov 2026 11:40:14 +0100 Subject: [PATCH 3/7] update agent skills based on latest implementation Signed-off-by: alperozturk96 --- .claude/skills/add-remote-endpoint/SKILL.md | 146 ++++++--- AGENTS.md | 337 +++++++++++--------- CLAUDE.md | 22 +- 3 files changed, 301 insertions(+), 204 deletions(-) diff --git a/.claude/skills/add-remote-endpoint/SKILL.md b/.claude/skills/add-remote-endpoint/SKILL.md index 5783ded148..e9793ee98e 100644 --- a/.claude/skills/add-remote-endpoint/SKILL.md +++ b/.claude/skills/add-remote-endpoint/SKILL.md @@ -1,6 +1,6 @@ --- name: add-remote-endpoint -description: Use when adding a new remote/network endpoint (a RemoteOperation / OCSRemoteOperation) to this Nextcloud Android library, or when the user says "add an endpoint", "new remote operation", "implement this API call", or "wire up API". Scaffolds the models, constants, operation, and tests following the governance package conventions and the standards in AGENTS.md. +description: Use when adding a new remote/network endpoint (a RemoteOperation on OcsKotlinRemoteOperation) to this Nextcloud Android library, or when the user says "add an endpoint", "new remote operation", "implement this API call", "create endpoints for ", or "wire up API". Scopes the calls to the client flow, then scaffolds verb/sub-feature packages, models, operations and integration tests following the teams package conventions and the standards in AGENTS.md. ---