diff --git a/.circleci/config.yml b/.circleci/config.yml index b2fd1db..f2e5c4a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ orbs: codacy: codacy/base@12.2.0 references: - cache_prefix: &cache_prefix sbt-cache-20240918 + cache_prefix: &cache_prefix sbt-cache-20250206 workflows: version: 2 diff --git a/src/main/scala/com/codacy/client/bitbucket/client/BitbucketClientBase.scala b/src/main/scala/com/codacy/client/bitbucket/client/BitbucketClientBase.scala index e166ff1..7e77068 100644 --- a/src/main/scala/com/codacy/client/bitbucket/client/BitbucketClientBase.scala +++ b/src/main/scala/com/codacy/client/bitbucket/client/BitbucketClientBase.scala @@ -28,6 +28,7 @@ abstract class BitbucketClientBase(val client: WSClient, credentials: Credential val usersBaseUrl = s"$apiBaseUrl/users" val repositoriesBaseUrl = s"$apiBaseUrl/repositories" val workspacesBaseUrl = s"$apiBaseUrl/workspaces" + val userWorkspacesBaseUrl = s"$userBaseUrl/workspaces" private lazy val requestTimeout = Duration(20, SECONDS) diff --git a/src/main/scala/com/codacy/client/bitbucket/v2/Workspace.scala b/src/main/scala/com/codacy/client/bitbucket/v2/Workspace.scala index 201f4ee..3e39157 100644 --- a/src/main/scala/com/codacy/client/bitbucket/v2/Workspace.scala +++ b/src/main/scala/com/codacy/client/bitbucket/v2/Workspace.scala @@ -3,13 +3,12 @@ package com.codacy.client.bitbucket.v2 import play.api.libs.functional.syntax._ import play.api.libs.json._ -case class Workspace(uuid: String, slug: String, name: String, avatar: String) +case class Workspace(uuid: String, slug: String, avatar: String) object Workspace { implicit val reader: Reads[Workspace] = ( (__ \ "uuid").read[String] and (__ \ "slug").read[String] and - (__ \ "name").read[String] and (__ \ "links" \ "avatar" \ "href").read[String] )(Workspace.apply _) } diff --git a/src/main/scala/com/codacy/client/bitbucket/v2/WorkspaceAccess.scala b/src/main/scala/com/codacy/client/bitbucket/v2/WorkspaceAccess.scala new file mode 100644 index 0000000..e4405ed --- /dev/null +++ b/src/main/scala/com/codacy/client/bitbucket/v2/WorkspaceAccess.scala @@ -0,0 +1,19 @@ +package com.codacy.client.bitbucket.v2 + +import play.api.libs.functional.syntax._ +import play.api.libs.json._ + +/** + * Represents a user's access to a workspace from the /user/workspaces endpoint. + * + * @param administrator Whether the user has administrator access to the workspace + * @param workspace The workspace details + */ +case class WorkspaceAccess(administrator: Boolean, workspace: Workspace) + +object WorkspaceAccess { + implicit val reader: Reads[WorkspaceAccess] = ( + (__ \ "administrator").read[Boolean] and + (__ \ "workspace").read[Workspace] + )(WorkspaceAccess.apply _) +} diff --git a/src/main/scala/com/codacy/client/bitbucket/v2/service/UserServices.scala b/src/main/scala/com/codacy/client/bitbucket/v2/service/UserServices.scala index 1575606..7c468f8 100644 --- a/src/main/scala/com/codacy/client/bitbucket/v2/service/UserServices.scala +++ b/src/main/scala/com/codacy/client/bitbucket/v2/service/UserServices.scala @@ -1,13 +1,7 @@ package com.codacy.client.bitbucket.v2.service import java.net.URLEncoder -import com.codacy.client.bitbucket.client.{ - BitbucketClient, - FailedResponse, - PageRequest, - RequestResponse, - SuccessfulResponse -} +import com.codacy.client.bitbucket.client.{BitbucketClient, RequestResponse} import com.codacy.client.bitbucket.v2._ import play.api.libs.json.Json @@ -26,26 +20,13 @@ class UserServices(client: BitbucketClient) { client.executePaginated[Email](s"${client.userBaseUrl}/emails") /** - * Search for workspaceUUID among the workspaces that the current user is member of - * returns the workspace and their effective role + * Get the current user's permission on a specific workspace. + * Uses the new /user/workspaces/{workspace}/permission endpoint + * (replaces deprecated /user/permissions/workspaces). */ def getWorkspaceMembership(workspaceUUID: String): RequestResponse[WorkspacePermission] = { val workspaceUUIDEncoded = URLEncoder.encode(s""""$workspaceUUID"""", "UTF-8") - val workspaceQuery = s"""workspace.uuid=$workspaceUUIDEncoded""" - - client.executeWithCursor[WorkspacePermission]( - s"""${client.userBaseUrl}/permissions/workspaces?q=$workspaceQuery""", - PageRequest() - ) match { - case SuccessfulResponse(membership +: _, _, _, _, _, _) => - if (membership.team.uuid == workspaceUUID) { - SuccessfulResponse(membership) - } else { - FailedResponse(s"UUIDs don't match. Queried $workspaceUUID but received ${membership.team.uuid}") - } - case error: FailedResponse => error - case _ => FailedResponse(s"Couldn't find $workspaceUUID for the current user") - } + client.execute[WorkspacePermission](s"${client.userBaseUrl}/workspaces/$workspaceUUIDEncoded/permission") } /** diff --git a/src/main/scala/com/codacy/client/bitbucket/v2/service/WorkspaceServices.scala b/src/main/scala/com/codacy/client/bitbucket/v2/service/WorkspaceServices.scala index 93719c4..86bf091 100644 --- a/src/main/scala/com/codacy/client/bitbucket/v2/service/WorkspaceServices.scala +++ b/src/main/scala/com/codacy/client/bitbucket/v2/service/WorkspaceServices.scala @@ -3,18 +3,32 @@ package com.codacy.client.bitbucket.v2.service import java.net.URLEncoder import com.codacy.client.bitbucket.util.UrlHelper._ import com.codacy.client.bitbucket.client.{BitbucketClient, PageRequest, RequestResponse} -import com.codacy.client.bitbucket.v2.{Project, UserIdentifiers, UserIdentifiersApi, Workspace, WorkspacePermission} +import com.codacy.client.bitbucket.v2.{ + Project, + UserIdentifiers, + UserIdentifiersApi, + Workspace, + WorkspaceAccess, + WorkspacePermission +} class WorkspaceServices(client: BitbucketClient) { - def list(pageRequest: Option[PageRequest] = None, pageLength: Option[Int] = None): RequestResponse[Seq[Workspace]] = { + /** + * Gets the list of workspaces accessible by the authenticated user. + * Uses the new /user/workspaces endpoint (replaces deprecated /workspaces). + */ + def list( + pageRequest: Option[PageRequest] = None, + pageLength: Option[Int] = None + ): RequestResponse[Seq[WorkspaceAccess]] = { pageRequest match { case Some(request) => - client.executeWithCursor[Workspace](client.workspacesBaseUrl, request, pageLength) + client.executeWithCursor[WorkspaceAccess](client.userWorkspacesBaseUrl, request, pageLength) case None => val length = pageLength.fold("")(pagelen => s"pagelen=$pagelen") - val urlWithPageLength = joinQueryParameters(client.workspacesBaseUrl, length) - client.executePaginated[Workspace](urlWithPageLength) + val urlWithPageLength = joinQueryParameters(client.userWorkspacesBaseUrl, length) + client.executePaginated[WorkspaceAccess](urlWithPageLength) } }