From da63796d14a552c4280ce06fea2a113d1d5c1561 Mon Sep 17 00:00:00 2001 From: Valeria Golovina Date: Tue, 23 Dec 2025 14:59:32 +0200 Subject: [PATCH] fix [gitlab]: Gitlab rest api calls now use projectId where possible * GitLabProjectCoordinates are not used as an argument for Api methods anymore * rest api methods use projectId to create request path (except for the initial project request) * initial project request uses encoded `owner/project` to get the path, and expected to be correctly redirected in case if the project was moved * fixes IJPL-81282 by using ID instead of full path in REST API calls * fixes project renaming issue (IJPL-82568) by calling REST endpoint with auto-redirect to get correct project path #IJPL-81282 Fixed #IJPL-82568 Fixed GitOrigin-RevId: 1529c3ddaa855539740a8c192e565e94b8cd9751 --- .../gitlab/api/GitLabProjectConnection.kt | 15 ++- .../api/GitLabProjectConnectionManager.kt | 34 ++++++- .../gitlab/api/GitLabProjectCoordinates.kt | 3 +- .../gitlab/api/dto/GitLabProjectRestDTO.kt | 6 ++ .../gitlab/api/request/GitLabProjectApi.kt | 42 ++++---- .../org/jetbrains/plugins/gitlab/api/util.kt | 11 +-- .../plugins/gitlab/data/GitLabImageLoader.kt | 6 +- .../request/GitLabMergeRequestChangesApi.kt | 23 +++-- .../request/GitLabMergeRequestCommentsApi.kt | 42 ++++---- .../GitLabMergeRequestDraftNotesApi.kt | 39 ++++---- .../request/GitLabMergeRequestReactionsApi.kt | 15 ++- .../api/request/GitLabMergeRequestsApi.kt | 85 +++++++++------- .../mergerequest/data/GitLabDiscussion.kt | 11 +-- .../mergerequest/data/GitLabMergeRequest.kt | 65 ++++++------ .../data/GitLabMergeRequestChanges.kt | 39 +++++--- .../GitLabMergeRequestDiscussionsContainer.kt | 21 ++-- .../gitlab/mergerequest/data/GitLabNote.kt | 21 ++-- .../gitlab/mergerequest/data/GitLabProject.kt | 42 ++++---- .../data/GitLabProjectMergeRequestsStore.kt | 38 ++++--- .../ui/GitLabMergeRequestViewModels.kt | 16 ++- ...itLabMergeRequestCreateComponentFactory.kt | 2 +- .../GitLabMergeRequestCreateDirectionModel.kt | 8 +- .../GitLabMergeRequestCreateViewModel.kt | 19 ++-- .../GitLabMergeRequestBranchesViewModel.kt | 26 ++--- .../GitLabMergeRequestChangeListViewModel.kt | 12 +-- .../GitLabMergeRequestDetailsViewModel.kt | 18 +++- ...ersistentMergeRequestChangesViewedState.kt | 32 +++--- .../GitLabMergeRequestDiffReviewViewModel.kt | 4 +- ...GitLabMergeRequestEditorReviewViewModel.kt | 15 ++- ...MergeRequestTimelineDiscussionViewModel.kt | 2 +- ...GitLabMergeRequestTimelineItemViewModel.kt | 2 +- .../GitLabMergeRequestTimelineViewModel.kt | 2 +- .../model/GitLabReviewTabViewModel.kt | 2 +- .../util/GitLabMergeRequestBranchUtil.kt | 49 ++++++--- .../GitLabPushNotificationCustomizer.kt | 99 +++++++++++-------- .../ui/GitLabMarkdownToHtmlConverter.kt | 15 ++- .../GitLabMergeRequestDiscussionViewModel.kt | 2 +- .../gitlab/ui/comment/GitLabNoteViewModel.kt | 2 +- .../plugins/gitlab/upload/GitLabUploadsApi.kt | 13 ++- .../plugins/gitlab/util/GitLabProjectPath.kt | 9 +- .../plugins/gitlab/util/gitLabStatistics.kt | 7 +- .../ui/GitLabMarkdownToHtmlConverterTest.kt | 15 ++- .../gitlab/util/GitLabProjectPathTest.kt | 86 ++++++++++++++++ .../plugins/gitlab/apitests/GitLabApiTest.kt | 78 ++++++++------- .../gitlab/apitests/GitLabApiTestCase.kt | 6 +- .../ui/GitLabShareProjectDialogViewModel.kt | 3 +- 46 files changed, 668 insertions(+), 434 deletions(-) create mode 100644 plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/dto/GitLabProjectRestDTO.kt create mode 100644 plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/util/GitLabProjectPathTest.kt diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnection.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnection.kt index f492f25f376c..8fb4ed77b678 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnection.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnection.kt @@ -20,11 +20,14 @@ import java.util.UUID /** * A low-level helper representing a GitLab project, to which the app was authorized to connect + * + * @param actualProjectCoordinates coordinates of the project e.g., after rename */ class GitLabProjectConnection( project: Project, private val scope: CoroutineScope, override val repo: GitLabProjectMapping, + actualProjectCoordinates: GitLabProjectCoordinates, glProject: GitLabProjectDTO, override val account: GitLabAccount, val currentUser: GitLabUserDTO, @@ -36,9 +39,17 @@ class GitLabProjectConnection( val tokenRefreshFlow: Flow = tokenState.drop(1).map { } - val imageLoader: GitLabImageLoader = GitLabImageLoader(apiClient, repo.repository) + val imageLoader: GitLabImageLoader = GitLabImageLoader(apiClient) - val projectData: GitLabProject = GitLabLazyProject(project, scope, apiClient, glMetadata, repo, glProject, currentUser, tokenRefreshFlow) + val projectData: GitLabProject = GitLabLazyProject(project, + scope, + apiClient, + glMetadata, + glProject, + currentUser, + tokenRefreshFlow, + actualProjectCoordinates, + repo.remote) val serverVersion: GitLabVersion? = glMetadata?.version diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnectionManager.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnectionManager.kt index a6cb6f257d01..208f765f4f35 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnectionManager.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectConnectionManager.kt @@ -11,11 +11,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import org.jetbrains.plugins.gitlab.GitLabProjectsManager +import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectDTO import org.jetbrains.plugins.gitlab.api.request.findProject import org.jetbrains.plugins.gitlab.api.request.getCurrentUser +import org.jetbrains.plugins.gitlab.api.request.getProject import org.jetbrains.plugins.gitlab.authentication.accounts.GitLabAccount import org.jetbrains.plugins.gitlab.authentication.accounts.GitLabAccountManager import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath @Service(Service.Level.PROJECT) internal class GitLabProjectConnectionManager(project: Project, cs: CoroutineScope) : @@ -28,12 +31,37 @@ internal class GitLabProjectConnectionManager(project: Project, cs: CoroutineSco { projectsManager }, { accountManager } ) { glProjectMapping, account, tokenState -> + val scope = this val apiClient = service().getClient(account.server) { tokenState.value } val glMetadata = apiClient.getMetadataOrNull() val currentUser = apiClient.graphQL.getCurrentUser() - val glProject = apiClient.graphQL.findProject(glProjectMapping.repository).body() - ?: throw error("Unable to load the project") - GitLabProjectConnection(project, this, glProjectMapping, glProject, account, currentUser, apiClient, glMetadata, tokenState) + + var projectCoordinates: GitLabProjectCoordinates = glProjectMapping.repository + val projectData: GitLabProjectDTO + val originalProject = apiClient.graphQL.findProject(projectCoordinates.projectPath).body() + if (originalProject != null) { + projectCoordinates = glProjectMapping.repository + projectData = originalProject + } + else { + val restProjectResponse = apiClient.rest.getProject(projectCoordinates.projectPath).body() + val actualProjectPath = GitLabProjectPath.extractProjectPath(restProjectResponse.pathWithNamespace) + ?: error("Unable to parse the project path: ${restProjectResponse.pathWithNamespace}") + projectCoordinates = projectCoordinates.copy(projectPath = actualProjectPath) + projectData = apiClient.graphQL.findProject(actualProjectPath).body() + ?: error("Could not find the project $actualProjectPath. Check if the project exists and you have access to it.") + } + + GitLabProjectConnection(project, + scope, + glProjectMapping, + projectCoordinates, + projectData, + account, + currentUser, + apiClient, + glMetadata, + tokenState) } private val delegate = SingleHostedGitRepositoryConnectionManagerImpl(cs, connectionFactory) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectCoordinates.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectCoordinates.kt index 3248963f43cc..c4b7664f8c7c 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectCoordinates.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/GitLabProjectCoordinates.kt @@ -20,8 +20,9 @@ data class GitLabProjectCoordinates(override val serverPath: GitLabServerPath, v companion object { fun create(server: GitLabServerPath, remote: GitRemoteUrlCoordinates): GitLabProjectCoordinates? { - val projectPath = GitLabProjectPath.create(server, remote) ?: return null + val projectPath = GitLabProjectPath.create(server, remote.url) ?: return null return GitLabProjectCoordinates(server, projectPath) } + } } \ No newline at end of file diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/dto/GitLabProjectRestDTO.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/dto/GitLabProjectRestDTO.kt new file mode 100644 index 000000000000..10991c80f7cd --- /dev/null +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/dto/GitLabProjectRestDTO.kt @@ -0,0 +1,6 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gitlab.api.dto + +data class GitLabProjectRestDTO( + val pathWithNamespace: String, +) \ No newline at end of file diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/request/GitLabProjectApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/request/GitLabProjectApi.kt index 8f482baf3d67..c453b534afb9 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/request/GitLabProjectApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/request/GitLabProjectApi.kt @@ -16,7 +16,6 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabGQLQuery import org.jetbrains.plugins.gitlab.api.GitLabId -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata import org.jetbrains.plugins.gitlab.api.GitLabVersion import org.jetbrains.plugins.gitlab.api.SinceGitLab @@ -27,24 +26,26 @@ import org.jetbrains.plugins.gitlab.api.dto.GitLabNamespaceRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectForCloneDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectIsForkedDTO +import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabProjectsForCloneDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabRepositoryCreationRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabUserRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO import org.jetbrains.plugins.gitlab.api.dto.WithGitLabNamespace import org.jetbrains.plugins.gitlab.api.gitLabQuery -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath import java.net.URI import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse @SinceGitLab("12.0") -suspend fun GitLabApi.GraphQL.findProject(project: GitLabProjectCoordinates): HttpResponse { +suspend fun GitLabApi.GraphQL.findProject(projectPath: GitLabProjectPath): HttpResponse { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), ) val request = gitLabQuery(GitLabGQLQuery.GET_PROJECT, parameters) return withErrorStats(GitLabGQLQuery.GET_PROJECT) { @@ -71,9 +72,9 @@ suspend fun GitLabApi.Rest.createProject( } @SinceGitLab("16.9") -private suspend fun GitLabApi.GraphQL.isProjectForked(project: GitLabProjectCoordinates): HttpResponse { +private suspend fun GitLabApi.GraphQL.isProjectForked(projectPath: GitLabProjectPath): HttpResponse { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), ) val request = gitLabQuery(GitLabGQLQuery.GET_PROJECT_IS_FORKED, parameters) return withErrorStats(GitLabGQLQuery.GET_PROJECT_IS_FORKED) { @@ -82,28 +83,35 @@ private suspend fun GitLabApi.GraphQL.isProjectForked(project: GitLabProjectCoor } @SinceGitLab("12.0") -private suspend fun GitLabApi.Rest.isProjectForked(project: GitLabProjectCoordinates): HttpResponse { - val uri = project.restApiUri - val request = request(uri).GET().build() +private suspend fun GitLabApi.Rest.isProjectForked(projectPath: GitLabProjectPath): HttpResponse { + val request = request(projectApiUrl(projectPath.fullPath())).GET().build() return withErrorStats(GitLabApiRequestName.REST_GET_PROJECT_IS_FORKED) { loadJsonValue(request) } } -suspend fun GitLabApi.isProjectForked(project: GitLabProjectCoordinates): Boolean = +@SinceGitLab("3.0", note = "Not an exact version") +suspend fun GitLabApi.Rest.getProject(projectPath: GitLabProjectPath): HttpResponse { + val request = request(projectApiUrl(projectPath.fullPath())).GET().build() + return withErrorStats(GitLabApiRequestName.REST_GET_PROJECT) { + loadJsonValue(request) + } +} + +suspend fun GitLabApi.isProjectForked(projectPath: GitLabProjectPath): Boolean = if (getMetadata().version < GitLabVersion(16, 9)) { - rest.isProjectForked(project).body().isForked + rest.isProjectForked(projectPath).body().isForked } else { - graphQL.isProjectForked(project).body() + graphQL.isProjectForked(projectPath).body() } @SinceGitLab("13.1", note = "No exact version") -fun GitLabApi.GraphQL.createAllProjectLabelsFlow(project: GitLabProjectCoordinates): Flow> = +fun GitLabApi.GraphQL.createAllProjectLabelsFlow(projectPath: GitLabProjectPath): Flow> = ApiPageUtil.createGQLPagesFlow { page -> val parameters = page.asParameters() + mapOf( - "fullPath" to project.projectPath.fullPath() + "fullPath" to projectPath.fullPath() ) val request = gitLabQuery(GitLabGQLQuery.GET_PROJECT_LABELS, parameters) withErrorStats(GitLabGQLQuery.GET_PROJECT_LABELS) { @@ -112,10 +120,10 @@ fun GitLabApi.GraphQL.createAllProjectLabelsFlow(project: GitLabProjectCoordinat }.map { it.nodes } @SinceGitLab("15.2") -fun GitLabApi.GraphQL.createAllWorkItemsFlow(project: GitLabProjectCoordinates): Flow> = +fun GitLabApi.GraphQL.createAllWorkItemsFlow(projectPath: GitLabProjectPath): Flow> = ApiPageUtil.createGQLPagesFlow { page -> val parameters = page.asParameters() + mapOf( - "fullPath" to project.projectPath.fullPath() + "fullPath" to projectPath.fullPath() ) val request = gitLabQuery(GitLabGQLQuery.GET_PROJECT_WORK_ITEMS, parameters) withErrorStats(GitLabGQLQuery.GET_PROJECT_WORK_ITEMS) { @@ -124,7 +132,7 @@ fun GitLabApi.GraphQL.createAllWorkItemsFlow(project: GitLabProjectCoordinates): }.map { it.nodes } @SinceGitLab("7.0", note = "No exact version") -fun getProjectUsersURI(project: GitLabProjectCoordinates) = project.restApiUri.resolveRelative("users") +fun GitLabApi.Rest.getProjectUsersURI(projectId: String): URI = projectApiUrl(projectId).resolveRelative("users") @SinceGitLab("7.0", note = "No exact version") suspend fun GitLabApi.Rest.getProjectUsers(uri: URI): HttpResponse> { diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/util.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/util.kt index 74d5efeedbf0..43b5eb6d088d 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/util.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/api/util.kt @@ -2,14 +2,11 @@ package org.jetbrains.plugins.gitlab.api import com.intellij.collaboration.util.resolveRelative -import org.jetbrains.plugins.gitlab.util.GitLabProjectPath import java.net.URI import java.net.URLEncoder -val GitLabProjectPath.apiId: String - get() = URLEncoder.encode("$owner/$name", Charsets.UTF_8) +fun GitLabApi.projectApiUrl(projectId: String): URI = server.projectApiUri(URLEncoder.encode(projectId, Charsets.UTF_8)) -val GitLabProjectCoordinates.restApiUri: URI - get() = serverPath.restApiUri - .resolveRelative("projects/") - .resolveRelative(projectPath.apiId + "/") \ No newline at end of file +fun GitLabServerPath.projectApiUri(projectId: String): URI = restApiUri + .resolveRelative("projects/") + .resolveRelative("$projectId/") diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/data/GitLabImageLoader.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/data/GitLabImageLoader.kt index b3f11d0e6367..c920f4d1c8b2 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/data/GitLabImageLoader.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/data/GitLabImageLoader.kt @@ -8,7 +8,6 @@ import com.intellij.util.IconUtil import com.intellij.util.ui.ImageUtil import icons.CollaborationToolsIcons import org.jetbrains.plugins.gitlab.api.GitLabApi -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO import org.jetbrains.plugins.gitlab.api.request.loadImage import java.awt.Image @@ -22,15 +21,14 @@ private val LOG = logger() private const val LOADED_GRAVATAR_SIZE: Int = 80 class GitLabImageLoader( - private val apiClient: GitLabApi, - private val gitLabProjectCoordinates: GitLabProjectCoordinates + private val apiClient: GitLabApi ) : AsyncImageIconsProvider.AsyncImageLoader, AsyncHtmlImageLoader { override suspend fun load(key: GitLabUserDTO): Image? { return key.avatarUrl?.let { avatarUrl -> val actualUri = when { avatarUrl.startsWith("http") -> avatarUrl avatarUrl.startsWith("/avatar") -> "https://secure.gravatar.com$avatarUrl?s=$LOADED_GRAVATAR_SIZE&d=identicon" - else -> gitLabProjectCoordinates.serverPath.uri + avatarUrl + else -> apiClient.server.uri + avatarUrl } load(null, actualUri) } diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestChangesApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestChangesApi.kt index 05837bae54a7..3345ee433424 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestChangesApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestChangesApi.kt @@ -5,13 +5,12 @@ import com.intellij.collaboration.api.json.loadJsonList import com.intellij.collaboration.api.json.loadJsonValue import com.intellij.collaboration.util.resolveRelative import org.jetbrains.plugins.gitlab.api.GitLabApi -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabCommitDetailedRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabCommitRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabDiffDTO import org.jetbrains.plugins.gitlab.api.getMetadataOrNull -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName @@ -19,8 +18,8 @@ import java.net.URI import java.net.http.HttpResponse @SinceGitLab("9.0", note = "Not an exact version") -suspend fun GitLabApi.getMergeRequestCommitsURI(project: GitLabProjectCoordinates, mrIid: String): URI { - return project.restApiUri +fun GitLabApi.Rest.getMergeRequestCommitsURI(projectId: String, mrIid: String): URI { + return projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("commits") @@ -48,11 +47,11 @@ suspend fun GitLabApi.Rest.loadMergeRequestChanges(uri: URI): HttpResponse { - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("repository") .resolveRelative("commits") .resolveRelative(commitSha) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestCommentsApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestCommentsApi.kt index 5b53a416a880..e62640865bb3 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestCommentsApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestCommentsApi.kt @@ -12,41 +12,41 @@ import com.intellij.collaboration.util.resolveRelative import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabApiUriQueryBuilder import org.jetbrains.plugins.gitlab.api.GitLabGQLQuery -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabCommitDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionRestDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabNoteRestDTO import org.jetbrains.plugins.gitlab.api.gitLabQuery import org.jetbrains.plugins.gitlab.api.loadList -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabDiffPositionInput import org.jetbrains.plugins.gitlab.mergerequest.api.dto.LineRangeDTO import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath import java.net.URI import java.net.http.HttpRequest import java.net.http.HttpResponse @SinceGitLab("10.6") suspend fun GitLabApi.Rest.loadMergeRequestDiscussions( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, ): HttpResponse> { - val uri = getMergeRequestDiscussionsUri(project, mrIid) + val uri = getMergeRequestDiscussionsUri(projectId, mrIid) return loadList(GitLabApiRequestName.REST_GET_MERGE_REQUEST_DISCUSSIONS, uri.toString()) } @SinceGitLab("14.7") suspend fun GitLabApi.GraphQL.loadMergeRequestCommits( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, pagination: GraphQLRequestPagination? = null ): GraphQLConnectionDTO? { val parameters = pagination.orDefault().asParameters() + mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mriid" to mrIid ) val request = gitLabQuery(GitLabGQLQuery.GET_MERGE_REQUEST_COMMITS, parameters) @@ -59,19 +59,19 @@ private class CommitConnection(pageInfo: GraphQLCursorPageInfoDTO, nodes: List(pageInfo, nodes) @SinceGitLab("10.6") -fun getMergeRequestDiscussionsUri(project: GitLabProjectCoordinates, mrIid: String): URI = - project.restApiUri +fun GitLabApi.Rest.getMergeRequestDiscussionsUri(projectId: String, mrIid: String): URI = + projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("discussions") @SinceGitLab("10.6") suspend fun GitLabApi.Rest.addNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, body: String, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid).withQuery { + val uri = getMergeRequestDiscussionsUri(projectId, mrIid).withQuery { "body" eq body } val request = request(uri).POST(HttpRequest.BodyPublishers.noBody()).build() @@ -82,12 +82,12 @@ suspend fun GitLabApi.Rest.addNote( @SinceGitLab("13.2") suspend fun GitLabApi.Rest.addDiffNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, position: GitLabDiffPositionInput, body: String, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid).withQuery { + val uri = getMergeRequestDiscussionsUri(projectId, mrIid).withQuery { "body" eq body addDiffPositionParameters(position) } @@ -99,12 +99,12 @@ suspend fun GitLabApi.Rest.addDiffNote( @SinceGitLab("10.6") suspend fun GitLabApi.Rest.createReplyNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, discussionId: String, body: String, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid) + val uri = getMergeRequestDiscussionsUri(projectId, mrIid) .resolveRelative(discussionId) .resolveRelative("notes") .withQuery { @@ -118,13 +118,13 @@ suspend fun GitLabApi.Rest.createReplyNote( @SinceGitLab("10.6") suspend fun GitLabApi.Rest.updateNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, discussionId: String, noteId: String, body: String, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid) + val uri = getMergeRequestDiscussionsUri(projectId, mrIid) .resolveRelative(discussionId) .resolveRelative("notes") .resolveRelative(noteId) @@ -139,12 +139,12 @@ suspend fun GitLabApi.Rest.updateNote( @SinceGitLab("10.6") suspend fun GitLabApi.Rest.deleteNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, discussionId: String, noteId: String, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid) + val uri = getMergeRequestDiscussionsUri(projectId, mrIid) .resolveRelative(discussionId) .resolveRelative("notes") .resolveRelative(noteId) @@ -156,12 +156,12 @@ suspend fun GitLabApi.Rest.deleteNote( @SinceGitLab("10.8") suspend fun GitLabApi.Rest.changeMergeRequestDiscussionResolve( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, discussionId: String, resolved: Boolean, ): HttpResponse { - val uri = getMergeRequestDiscussionsUri(project, mrIid) + val uri = getMergeRequestDiscussionsUri(projectId, mrIid) .resolveRelative(discussionId) .withQuery { "resolved" eq resolved @@ -211,5 +211,3 @@ internal fun GitLabApiUriQueryBuilder.addLineRangeParameters(lineRange: LineRang } } } - - diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestDraftNotesApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestDraftNotesApi.kt index d780aed73ebe..9046db71f07a 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestDraftNotesApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestDraftNotesApi.kt @@ -5,10 +5,9 @@ import com.intellij.collaboration.api.json.loadJsonValue import com.intellij.collaboration.util.resolveRelative import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabApiUriQueryBuilder -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabMergeRequestDraftNoteRestDTO -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabDiffPositionInput @@ -18,25 +17,29 @@ import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse @SinceGitLab("15.9") -fun getMergeRequestDraftNotesUri(project: GitLabProjectCoordinates, mrIid: String): URI = - project.restApiUri +fun GitLabApi.Rest.getMergeRequestDraftNotesUri(projectId: String, mrIid: String): URI = + projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("draft_notes") -private fun getSpecificMergeRequestDraftNoteUri(project: GitLabProjectCoordinates, mrIid: String, noteId: String): URI = - getMergeRequestDraftNotesUri(project, mrIid).resolveRelative(noteId) +private fun GitLabApi.Rest.getSpecificMergeRequestDraftNoteUri( + projectId: String, + mrIid: String, + noteId: String, +): URI = + getMergeRequestDraftNotesUri(projectId, mrIid).resolveRelative(noteId) @SinceGitLab("15.10") suspend fun GitLabApi.Rest.updateDraftNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, noteId: String, position: GitLabMergeRequestDraftNoteRestDTO.Position, body: String, ) : HttpResponse { - val uri = getSpecificMergeRequestDraftNoteUri(project, mrIid, noteId).withQuery { + val uri = getSpecificMergeRequestDraftNoteUri(projectId, mrIid, noteId).withQuery { "note" eq body addDraftNotePositionParameters(position) // have to pass the existing position, otherwise it is reset to null } @@ -48,12 +51,12 @@ suspend fun GitLabApi.Rest.updateDraftNote( @SinceGitLab("15.9") suspend fun GitLabApi.Rest.deleteDraftNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, noteId: Long, ) : HttpResponse { - val uri = getSpecificMergeRequestDraftNoteUri(project, mrIid, noteId.toString()) + val uri = getSpecificMergeRequestDraftNoteUri(projectId, mrIid, noteId.toString()) val request = request(uri).DELETE().build() return withErrorStats(GitLabApiRequestName.REST_DELETE_DRAFT_NOTE) { sendAndAwaitCancellable(request) @@ -62,11 +65,11 @@ suspend fun GitLabApi.Rest.deleteDraftNote( @SinceGitLab("15.11") suspend fun GitLabApi.Rest.submitDraftNotes( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, ) : HttpResponse { - val uri = getMergeRequestDraftNotesUri(project, mrIid).resolveRelative("bulk_publish") + val uri = getMergeRequestDraftNotesUri(projectId, mrIid).resolveRelative("bulk_publish") val request = request(uri).POST(BodyPublishers.noBody()).build() return withErrorStats(GitLabApiRequestName.REST_SUBMIT_DRAFT_NOTES) { sendAndAwaitCancellable(request) @@ -75,11 +78,11 @@ suspend fun GitLabApi.Rest.submitDraftNotes( @SinceGitLab("15.10") suspend fun GitLabApi.Rest.submitSingleDraftNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, noteId: Long, ): HttpResponse { - val uri = getSpecificMergeRequestDraftNoteUri(project, mrIid, noteId.toString()).resolveRelative("publish") + val uri = getSpecificMergeRequestDraftNoteUri(projectId, mrIid, noteId.toString()).resolveRelative("publish") val request = request(uri).PUT(BodyPublishers.noBody()).build() return withErrorStats(GitLabApiRequestName.REST_SUBMIT_SINGLE_DRAFT_NOTE) { sendAndAwaitCancellable(request) @@ -88,12 +91,12 @@ suspend fun GitLabApi.Rest.submitSingleDraftNote( @SinceGitLab("16.3") suspend fun GitLabApi.Rest.addDraftReplyNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, discussionId: String, body: String, ): HttpResponse { - val uri = getMergeRequestDraftNotesUri(project, mrIid).withQuery { + val uri = getMergeRequestDraftNotesUri(projectId, mrIid).withQuery { "note" eq body "in_reply_to_discussion_id" eq discussionId } @@ -105,13 +108,13 @@ suspend fun GitLabApi.Rest.addDraftReplyNote( @SinceGitLab("15.10") suspend fun GitLabApi.Rest.addDraftNote( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, @SinceGitLab("16.3") positionOrNull: GitLabDiffPositionInput?, body: String, ): HttpResponse { - val uri = getMergeRequestDraftNotesUri(project, mrIid).withQuery { + val uri = getMergeRequestDraftNotesUri(projectId, mrIid).withQuery { "note" eq body positionOrNull?.let { addDiffPositionParameters(it) } } diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestReactionsApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestReactionsApi.kt index 7a80f4ed406f..55c39173d01e 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestReactionsApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestReactionsApi.kt @@ -4,10 +4,9 @@ package org.jetbrains.plugins.gitlab.mergerequest.api.request import com.intellij.collaboration.api.json.loadJsonValue import com.intellij.collaboration.util.resolveRelative import org.jetbrains.plugins.gitlab.api.GitLabApi -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabAwardEmojiRestDTO -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName @@ -16,8 +15,8 @@ import java.net.http.HttpRequest import java.net.http.HttpResponse @SinceGitLab("8.9") -fun getMRNotesAwardEmojiUri(project: GitLabProjectCoordinates, mrIid: String, noteId: String): URI = - project.restApiUri +fun GitLabApi.Rest.getMRNotesAwardEmojiUri(projectId: String, mrIid: String, noteId: String): URI = + projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("notes") @@ -26,12 +25,12 @@ fun getMRNotesAwardEmojiUri(project: GitLabProjectCoordinates, mrIid: String, no @SinceGitLab("8.9") suspend fun GitLabApi.Rest.addAwardEmoji( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, noteId: String, name: String, ): HttpResponse { - val uri = getMRNotesAwardEmojiUri(project, mrIid, noteId).withQuery { + val uri = getMRNotesAwardEmojiUri(projectId, mrIid, noteId).withQuery { "name" eq name } val request = request(uri).POST(HttpRequest.BodyPublishers.noBody()).build() @@ -42,12 +41,12 @@ suspend fun GitLabApi.Rest.addAwardEmoji( @SinceGitLab("8.9") suspend fun GitLabApi.Rest.deleteAwardEmoji( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, noteId: String, awardId: String, ): HttpResponse { - val uri = getMRNotesAwardEmojiUri(project, mrIid, noteId) + val uri = getMRNotesAwardEmojiUri(projectId, mrIid, noteId) .resolveRelative(awardId) val request = request(uri).DELETE().build() return withErrorStats(GitLabApiRequestName.REST_DELETE_NOTE_AWARD_EMOJI) { diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestsApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestsApi.kt index 9aef7e45fd31..257fb7e9903a 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestsApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/api/request/GitLabMergeRequestsApi.kt @@ -12,13 +12,12 @@ import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabEdition import org.jetbrains.plugins.gitlab.api.GitLabGQLQuery import org.jetbrains.plugins.gitlab.api.GitLabGidData -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabGraphQLMutationResultDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabReviewerDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO import org.jetbrains.plugins.gitlab.api.gitLabQuery -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import org.jetbrains.plugins.gitlab.api.withErrorStats import org.jetbrains.plugins.gitlab.api.withQuery import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestByBranchDTO @@ -29,6 +28,7 @@ import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestShort import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestState import org.jetbrains.plugins.gitlab.mergerequest.data.asApiParameter import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath import java.net.URI import java.net.http.HttpRequest import java.net.http.HttpResponse @@ -42,7 +42,7 @@ import java.net.http.HttpResponse */ @SinceGitLab("14.0", note = "No exact version, but definitely exists in minimal") suspend fun GitLabApi.Rest.createMergeRequest( - project: GitLabProjectCoordinates, + projectId: String, sourceBranch: String, targetBranch: String, title: String, @@ -51,7 +51,7 @@ suspend fun GitLabApi.Rest.createMergeRequest( assigneeIds: List? = null, labels: List? = null ): HttpResponse { - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("merge_requests") .withQuery { "source_branch" eq sourceBranch @@ -69,17 +69,19 @@ suspend fun GitLabApi.Rest.createMergeRequest( } @SinceGitLab("7.0", note = "?search available since 10.4, ?scope since 9.5") -fun getMergeRequestListURI(project: GitLabProjectCoordinates, - searchQuery: String): URI = - project.restApiUri.resolveRelative("merge_requests").withQuery(searchQuery) +fun GitLabApi.Rest.getMergeRequestListURI( + projectId: String, + searchQuery: String +): URI = + projectApiUrl(projectId).resolveRelative("merge_requests").withQuery(searchQuery) @SinceGitLab("12.0") suspend fun GitLabApi.GraphQL.loadMergeRequest( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String ): HttpResponse { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid ) val request = gitLabQuery(GitLabGQLQuery.GET_MERGE_REQUEST, parameters) @@ -91,11 +93,11 @@ suspend fun GitLabApi.GraphQL.loadMergeRequest( @ApiStatus.Internal @SinceGitLab("14.0", note = "No exact version") suspend fun GitLabApi.GraphQL.getMergeRequestMetrics( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, username: String, ): HttpResponse { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "username" to username ) val request = gitLabQuery(GitLabGQLQuery.GET_MERGE_REQUEST_METRICS, parameters) @@ -106,13 +108,13 @@ suspend fun GitLabApi.GraphQL.getMergeRequestMetrics( @SinceGitLab("13.1") suspend fun GitLabApi.GraphQL.findMergeRequestsByBranch( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, state: GitLabMergeRequestState, sourceBranch: String, targetBranch: String? = null ): HttpResponse?> { val parameters = mutableMapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "state" to state.asApiParameter(), "sourceBranches" to listOf(sourceBranch), "targetBranches" to targetBranch?.let { listOf(it) } @@ -127,24 +129,33 @@ private class MergeRequestsByBranchConnection(pageInfo: GraphQLCursorPageInfoDTO : GraphQLConnectionDTO(pageInfo, nodes) @SinceGitLab("13.2") -fun getMergeRequestStateEventsUri(project: GitLabProjectCoordinates, mrIid: String): URI = - project.restApiUri.resolveRelative("merge_requests").resolveRelative(mrIid).resolveRelative("resource_state_events") +fun GitLabApi.Rest.getMergeRequestStateEventsUri(projectId: String, mrIid: String): URI = + projectApiUrl(projectId) + .resolveRelative("merge_requests") + .resolveRelative(mrIid) + .resolveRelative("resource_state_events") @SinceGitLab("11.4", note = "Maybe released in 11.3-rc5") -fun getMergeRequestLabelEventsUri(project: GitLabProjectCoordinates, mrIid: String): URI = - project.restApiUri.resolveRelative("merge_requests").resolveRelative(mrIid).resolveRelative("resource_label_events") +fun GitLabApi.Rest.getMergeRequestLabelEventsUri(projectId: String, mrIid: String): URI = + projectApiUrl(projectId) + .resolveRelative("merge_requests") + .resolveRelative(mrIid) + .resolveRelative("resource_label_events") @SinceGitLab("13.1") -fun getMergeRequestMilestoneEventsUri(project: GitLabProjectCoordinates, mrIid: String): URI = - project.restApiUri.resolveRelative("merge_requests").resolveRelative(mrIid).resolveRelative("resource_milestone_events") +fun GitLabApi.Rest.getMergeRequestMilestoneEventsUri(projectId: String, mrIid: String): URI = + projectApiUrl(projectId) + .resolveRelative("merge_requests") + .resolveRelative(mrIid) + .resolveRelative("resource_milestone_events") @SinceGitLab("10.6", editions = [GitLabEdition.Enterprise]) @SinceGitLab("13.3", editions = [GitLabEdition.Community], note = "Maybe released in 13.2-rc42 or so") suspend fun GitLabApi.Rest.mergeRequestApprove( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String ): HttpResponse { - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("approve") @@ -157,10 +168,10 @@ suspend fun GitLabApi.Rest.mergeRequestApprove( @SinceGitLab("10.6", editions = [GitLabEdition.Enterprise]) @SinceGitLab("13.3", editions = [GitLabEdition.Community], note = "Maybe released in 13.2-rc42 or so") suspend fun GitLabApi.Rest.mergeRequestUnApprove( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String ): HttpResponse { - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("unapprove") @@ -172,10 +183,10 @@ suspend fun GitLabApi.Rest.mergeRequestUnApprove( @SinceGitLab("11.6") suspend fun GitLabApi.Rest.mergeRequestRebase( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String ): HttpResponse { - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .resolveRelative("rebase") @@ -187,12 +198,12 @@ suspend fun GitLabApi.Rest.mergeRequestRebase( @SinceGitLab("13.9") suspend fun GitLabApi.GraphQL.mergeRequestUpdate( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, state: GitLabMergeRequestNewState, ): HttpResponse?> { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid, "state" to state ) @@ -211,12 +222,12 @@ suspend fun GitLabApi.GraphQL.mergeRequestUpdate( */ @SinceGitLab("13.8") suspend fun GitLabApi.Rest.mergeRequestSetReviewers( - project: GitLabProjectCoordinates, + projectId: String, mrIid: String, reviewers: List ): HttpResponse { val reviewerIds = reviewers.map { GitLabGidData(it.id).guessRestId() } - val uri = project.restApiUri + val uri = projectApiUrl(projectId) .resolveRelative("merge_requests") .resolveRelative(mrIid) .withQuery { @@ -238,12 +249,12 @@ suspend fun GitLabApi.Rest.mergeRequestSetReviewers( */ @SinceGitLab("15.3") suspend fun GitLabApi.GraphQL.mergeRequestSetReviewers( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, reviewers: List ): HttpResponse?> { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid, "reviewerUsernames" to reviewers.map { it.username } ) @@ -255,7 +266,7 @@ suspend fun GitLabApi.GraphQL.mergeRequestSetReviewers( @SinceGitLab("13.10") suspend fun GitLabApi.GraphQL.mergeRequestAccept( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, commitMessage: String, sha: String, @@ -263,7 +274,7 @@ suspend fun GitLabApi.GraphQL.mergeRequestAccept( shouldRemoveSourceBranch: Boolean ): HttpResponse?> { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid, "commitMessage" to commitMessage, "sha" to sha, @@ -278,12 +289,12 @@ suspend fun GitLabApi.GraphQL.mergeRequestAccept( @SinceGitLab("13.12") suspend fun GitLabApi.GraphQL.mergeRequestSetDraft( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, isDraft: Boolean ): HttpResponse?> { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid, "isDraft" to isDraft ) @@ -295,12 +306,12 @@ suspend fun GitLabApi.GraphQL.mergeRequestSetDraft( @SinceGitLab("13.9") suspend fun GitLabApi.GraphQL.mergeRequestReviewerRereview( - project: GitLabProjectCoordinates, + projectPath: GitLabProjectPath, mrIid: String, reviewer: GitLabReviewerDTO ): HttpResponse?> { val parameters = mapOf( - "projectId" to project.projectPath.fullPath(), + "projectId" to projectPath.fullPath(), "mergeRequestId" to mrIid, "userId" to reviewer.id ) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabDiscussion.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabDiscussion.kt index a3e6eafd5ad0..e362032b6937 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabDiscussion.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabDiscussion.kt @@ -33,7 +33,6 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabId -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabRestId import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata import org.jetbrains.plugins.gitlab.api.GitLabVersion @@ -77,7 +76,7 @@ class LoadedGitLabDiscussion( parentCs: CoroutineScope, private val api: GitLabApi, glMetadata: GitLabServerMetadata?, - private val glProject: GitLabProjectCoordinates, + private val projectId: String, private val currentUser: GitLabUserDTO, private val eventSink: suspend (Change) -> Unit, private val draftNotesEventSink: suspend (Change) -> Unit, @@ -131,7 +130,7 @@ class LoadedGitLabDiscussion( .mapDataToModel( GitLabNoteRestDTO::id, { note -> - MutableGitLabMergeRequestNote(this, api, glProject, mr, id, currentUser, noteEvents::emit, note) + MutableGitLabMergeRequestNote(this, api, projectId, mr, id, currentUser, noteEvents::emit, note) }, MutableGitLabMergeRequestNote::update ).combine(draftNotes) { notes, draftNotes -> @@ -160,7 +159,7 @@ class LoadedGitLabDiscussion( operationsGuard.withLock { val resolved = resolved.first() val result = withContext(Dispatchers.IO) { - api.rest.changeMergeRequestDiscussionResolve(glProject, mr.iid, id.restId, !resolved).body() + api.rest.changeMergeRequestDiscussionResolve(projectId, mr.iid, id.restId, !resolved).body() } noteEvents.emit(GitLabNoteEvent.Changed(result.notes)) if (mr.details.value.targetProject.onlyAllowMergeIfAllDiscussionsAreResolved) { @@ -173,7 +172,7 @@ class LoadedGitLabDiscussion( override suspend fun addNote(body: String) { withContext(cs.coroutineContext) { val note = withContext(Dispatchers.IO) { - api.rest.createReplyNote(glProject, mr.iid, id.restId, body).body() + api.rest.createReplyNote(projectId, mr.iid, id.restId, body).body() } withContext(NonCancellable) { @@ -185,7 +184,7 @@ class LoadedGitLabDiscussion( override suspend fun addDraftNote(body: String) { withContext(cs.coroutineContext) { withContext(Dispatchers.IO) { - api.rest.addDraftReplyNote(glProject, mr.iid, id.restId, body).body() + api.rest.addDraftReplyNote(projectId, mr.iid, id.restId, body).body() }?.also { withContext(NonCancellable) { draftNotesEventSink(AddedLast(it)) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequest.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequest.kt index be0c95fde702..34b51d1c8d87 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequest.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequest.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project import git4idea.GitStandardRemoteBranch import git4idea.changes.GitBranchComparisonResult +import git4idea.remote.GitRemoteUrlCoordinates import git4idea.remote.hosting.GitRemoteBranchesUtil import git4idea.remote.hosting.changesSignalFlow import git4idea.repo.GitRepository @@ -38,6 +39,7 @@ import kotlinx.coroutines.withContext import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata +import org.jetbrains.plugins.gitlab.api.GitLabServerPath import org.jetbrains.plugins.gitlab.api.GitLabVersion import org.jetbrains.plugins.gitlab.api.dto.GitLabResourceLabelEventDTO import org.jetbrains.plugins.gitlab.api.dto.GitLabResourceMilestoneEventDTO @@ -62,7 +64,6 @@ import org.jetbrains.plugins.gitlab.mergerequest.api.request.mergeRequestUnAppro import org.jetbrains.plugins.gitlab.mergerequest.api.request.mergeRequestUpdate import org.jetbrains.plugins.gitlab.mergerequest.data.loaders.startGitLabRestETagListLoaderIn import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import org.jetbrains.plugins.gitlab.util.GitLabRegistry import org.jetbrains.plugins.gitlab.util.GitLabStatistics @@ -70,8 +71,9 @@ private val LOG = logger() @CodeReviewDomainEntity interface GitLabMergeRequest : GitLabMergeRequestDiscussionsContainer { - val glProject: GitLabProjectCoordinates - val gitRepository: GitRepository + val serverPath: GitLabServerPath + val gitRemote: GitRemoteUrlCoordinates + val projectId: String val iid: String val gid: String @@ -142,14 +144,15 @@ internal class LoadedGitLabMergeRequest( parentCs: CoroutineScope, private val api: GitLabApi, private val glMetadata: GitLabServerMetadata?, - private val projectMapping: GitLabProjectMapping, - private val currentUser: GitLabUserDTO, - mergeRequest: GitLabMergeRequestDTO + private val projectCoordinates: GitLabProjectCoordinates, + override val projectId: String, + override val gitRemote: GitRemoteUrlCoordinates, + currentUser: GitLabUserDTO, + mergeRequest: GitLabMergeRequestDTO, ) : GitLabMergeRequest { private val cs = parentCs.childScope(this::class, Dispatchers.Default) - override val glProject: GitLabProjectCoordinates = projectMapping.repository - override val gitRepository: GitRepository = projectMapping.gitRepository + override val serverPath: GitLabServerPath = projectCoordinates.serverPath override val iid: String = mergeRequest.iid override val gid: String = mergeRequest.id @@ -167,13 +170,16 @@ internal class LoadedGitLabMergeRequest( override val changes: SharedFlow = mergeRequestDetailsState .distinctUntilChangedBy(GitLabMergeRequestFullDetails::diffRefs) - .mapScoped { details -> GitLabMergeRequestChangesImpl(this, api, glMetadata, projectMapping, details) } + .mapScoped { details -> + GitLabMergeRequestChangesImpl(this, project, projectId, projectCoordinates.projectPath, gitRemote, + api, glMetadata, details) + } .modelFlow(cs, LOG) override val stateEvents by lazy { startGitLabRestETagListLoaderIn( cs, - getMergeRequestStateEventsUri(glProject, iid), + api.rest.getMergeRequestStateEventsUri(projectId, iid), { it.id }, requestReloadFlow = mergeRequestReloadRequest.withInitial(Unit), @@ -189,7 +195,7 @@ internal class LoadedGitLabMergeRequest( override val labelEvents by lazy { startGitLabRestETagListLoaderIn( cs, - getMergeRequestLabelEventsUri(glProject, iid), + api.rest.getMergeRequestLabelEventsUri(projectId, iid), { it.id }, requestReloadFlow = mergeRequestReloadRequest.withInitial(Unit), @@ -205,7 +211,7 @@ internal class LoadedGitLabMergeRequest( override val milestoneEvents by lazy { startGitLabRestETagListLoaderIn( cs, - getMergeRequestMilestoneEventsUri(glProject, iid), + api.rest.getMergeRequestMilestoneEventsUri(projectId, iid), { it.id }, requestReloadFlow = mergeRequestReloadRequest.withInitial(Unit), @@ -225,7 +231,7 @@ internal class LoadedGitLabMergeRequest( override val draftReviewText: MutableStateFlow = MutableStateFlow("") private val discussionsContainer = - GitLabMergeRequestDiscussionsContainerImpl(parentCs, project, api, glMetadata, projectMapping.repository, currentUser, this) + GitLabMergeRequestDiscussionsContainerImpl(parentCs, project, api, glMetadata, projectId, currentUser, this) init { cs.launch { @@ -237,7 +243,7 @@ internal class LoadedGitLabMergeRequest( } cs.launch { - val repository = projectMapping.gitRepository + val repository = gitRemote.repository repository.changesSignalFlow().withInitial(Unit).combine(details) { _, currentDetails -> isCurrentDataInSyncWithRepository(currentDetails, repository) }.distinctUntilChanged().filterNotNull().collectLatest { @@ -255,7 +261,7 @@ internal class LoadedGitLabMergeRequest( detailsLoadingGuard.lock() _isLoading.value = true val updatedMergeRequest = withContext(Dispatchers.IO) { - api.graphQL.loadMergeRequest(glProject, iid).body()!! + api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body()!! } return updateMergeRequestData(updatedMergeRequest) } @@ -266,7 +272,7 @@ internal class LoadedGitLabMergeRequest( } private fun isCurrentDataInSyncWithRepository(details: GitLabMergeRequestFullDetails, repository: GitRepository): Boolean? { - val remoteMrBranchHash = details.getSourceRemoteDescriptor(projectMapping.repository.serverPath)?.let { + val remoteMrBranchHash = details.getSourceRemoteDescriptor(serverPath)?.let { GitRemoteBranchesUtil.findRemote(repository, it) }?.let { val branch = GitStandardRemoteBranch(it, details.sourceBranch) @@ -329,7 +335,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun approve() { try { withContext(cs.coroutineContext + Dispatchers.IO) { - api.rest.mergeRequestApprove(glProject, iid) + api.rest.mergeRequestApprove(projectId, iid) } } finally { @@ -341,7 +347,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun unApprove() { try { withContext(cs.coroutineContext + Dispatchers.IO) { - api.rest.mergeRequestUnApprove(glProject, iid) + api.rest.mergeRequestUnApprove(projectId, iid) } } finally { @@ -352,7 +358,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun close() { withContext(cs.coroutineContext + Dispatchers.IO) { - val updatedMergeRequest = api.graphQL.mergeRequestUpdate(glProject, iid, GitLabMergeRequestNewState.CLOSED) + val updatedMergeRequest = api.graphQL.mergeRequestUpdate(projectCoordinates.projectPath, iid, GitLabMergeRequestNewState.CLOSED) .getResultOrThrow() updateMergeRequestData(updatedMergeRequest) stateEventsRefreshRequest.emit(Unit) @@ -362,7 +368,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun reopen() { withContext(cs.coroutineContext + Dispatchers.IO) { - val updatedMergeRequest = api.graphQL.mergeRequestUpdate(glProject, iid, GitLabMergeRequestNewState.OPEN) + val updatedMergeRequest = api.graphQL.mergeRequestUpdate(projectCoordinates.projectPath, iid, GitLabMergeRequestNewState.OPEN) .getResultOrThrow() updateMergeRequestData(updatedMergeRequest) stateEventsRefreshRequest.emit(Unit) @@ -372,7 +378,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun postReview() { withContext(cs.coroutineContext + Dispatchers.IO) { - val updatedMergeRequest = api.graphQL.mergeRequestSetDraft(glProject, iid, isDraft = false) + val updatedMergeRequest = api.graphQL.mergeRequestSetDraft(projectCoordinates.projectPath, iid, isDraft = false) .getResultOrThrow() updateMergeRequestData(updatedMergeRequest) } @@ -383,11 +389,11 @@ internal class LoadedGitLabMergeRequest( override suspend fun setReviewers(reviewers: List) { withContext(cs.coroutineContext + Dispatchers.IO) { val updatedMergeRequest = if (glMetadata != null && GitLabVersion(15, 3) <= glMetadata.version) { - api.graphQL.mergeRequestSetReviewers(glProject, iid, reviewers).getResultOrThrow() + api.graphQL.mergeRequestSetReviewers(projectCoordinates.projectPath, iid, reviewers).getResultOrThrow() } else { - api.rest.mergeRequestSetReviewers(glProject, iid, reviewers).body() - api.graphQL.loadMergeRequest(glProject, iid).body() ?: error("Merge request could not be loaded") + api.rest.mergeRequestSetReviewers(projectId, iid, reviewers).body() + api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body() ?: error("Merge request could not be loaded") } updateMergeRequestData(updatedMergeRequest) @@ -399,7 +405,7 @@ internal class LoadedGitLabMergeRequest( override suspend fun reviewerRereview(reviewers: Collection) { withContext(cs.coroutineContext + Dispatchers.IO) { reviewers.forEach { reviewer -> - val updatedMergeRequest = api.graphQL.mergeRequestReviewerRereview(glProject, iid, reviewer) + val updatedMergeRequest = api.graphQL.mergeRequestReviewerRereview(projectCoordinates.projectPath, iid, reviewer) .getResultOrThrow() updateMergeRequestData(updatedMergeRequest) } @@ -442,9 +448,10 @@ internal class LoadedGitLabMergeRequest( val sha = mergeRequestDetailsState.value.diffRefs?.headSha ?: return val shouldRemoveSourceBranch = mergeRequestDetailsState.value.shouldRemoveSourceBranch ?: mergeRequestDetailsState.value.targetProject.removeSourceBranchAfterMerge - api.graphQL.mergeRequestAccept(glProject, iid, commitMessage, sha, withSquash, shouldRemoveSourceBranch).getResultOrThrow() + api.graphQL.mergeRequestAccept(projectCoordinates.projectPath, iid, commitMessage, sha, withSquash, shouldRemoveSourceBranch) + .getResultOrThrow() do { - val updatedMergeRequest = api.graphQL.loadMergeRequest(glProject, iid).body()!! + val updatedMergeRequest = api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body()!! updateMergeRequestData(updatedMergeRequest) delay(GitLabRegistry.getRequestPollingIntervalMillis().toLong()) attempts++ @@ -454,9 +461,9 @@ internal class LoadedGitLabMergeRequest( private suspend fun runRebase() { var attempts = 0 - api.rest.mergeRequestRebase(glProject, iid) + api.rest.mergeRequestRebase(projectId, iid) do { - val updatedMergeRequest = api.graphQL.loadMergeRequest(glProject, iid).body()!! + val updatedMergeRequest = api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body()!! updateMergeRequestData(updatedMergeRequest) delay(GitLabRegistry.getRequestPollingIntervalMillis().toLong()) attempts++ diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestChanges.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestChanges.kt index dc1dc8325e1b..4c389ad10226 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestChanges.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestChanges.kt @@ -7,12 +7,14 @@ import com.intellij.collaboration.async.childScope import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diff.impl.patch.PatchReader import com.intellij.openapi.diff.impl.patch.TextFilePatch +import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.FileStatus import com.intellij.vcsUtil.VcsFileUtil import git4idea.changes.GitBranchComparisonResult import git4idea.changes.GitCommitShaWithPatches import git4idea.changes.filePath +import git4idea.remote.GitRemoteUrlCoordinates import git4idea.remote.hosting.GitCodeReviewUtils import git4idea.repo.GitRepository import kotlinx.coroutines.CoroutineScope @@ -38,7 +40,7 @@ import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadCommitDiffs import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestChanges import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestCommits import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestDiffs -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath private val LOG = logger() @@ -74,26 +76,29 @@ internal suspend fun GitLabMergeRequestChanges.loadRevisionsAndParseChanges(): G class GitLabMergeRequestChangesImpl( parentCs: CoroutineScope, + private val project: Project, + private val projectId: String, + private val projectPath: GitLabProjectPath, + private val gitRemoteUrlCoordinates: GitRemoteUrlCoordinates, private val api: GitLabApi, private val glMetadata: GitLabServerMetadata?, - private val projectMapping: GitLabProjectMapping, private val mergeRequestDetails: GitLabMergeRequestFullDetails, ) : GitLabMergeRequestChanges { private val cs = parentCs.childScope(this::class) - private val glProject = projectMapping.repository - private val commits: Deferred> = cs.async { if (glMetadata != null && glMetadata.version < GitLabVersion(14, 7)) { - val initialURI = api.getMergeRequestCommitsURI(glProject, mergeRequestDetails.iid) + val initialURI = api.rest.getMergeRequestCommitsURI(projectId, mergeRequestDetails.iid) return@async ApiPageUtil.createPagesFlowByLinkHeader(initialURI) { uri -> api.rest.loadMergeRequestCommits(uri) } .map { it.body() ?: emptyList() } .foldToList(GitLabCommit.Companion::fromRestDTO) .asReversed() } - ApiPageUtil.createGQLPagesFlow { pagination -> api.graphQL.loadMergeRequestCommits(glProject, mergeRequestDetails.iid, pagination) } + ApiPageUtil.createGQLPagesFlow { pagination -> + api.graphQL.loadMergeRequestCommits(projectPath, mergeRequestDetails.iid, pagination) + } .map { page -> page.nodes } .foldToList(GitLabCommit.Companion::fromGraphQLDTO) .asReversed() @@ -108,7 +113,6 @@ class GitLabMergeRequestChangesImpl( override suspend fun getParsedChanges(): GitBranchComparisonResult = parsedChanges.await() private suspend fun loadChanges(commits: List): GitBranchComparisonResult { - val repository = projectMapping.remote.repository val diffRefs = mergeRequestDetails.diffRefs ?: error("Missing diff refs") val baseSha = diffRefs.startSha val mergeBaseSha = diffRefs.baseSha ?: error("Missing merge base revision") @@ -117,8 +121,8 @@ class GitLabMergeRequestChangesImpl( coroutineScope { commits.map { commit -> async { - val commitWithParents = api.rest.loadCommit(glProject, commit.sha).body()!! - val patches = ApiPageUtil.createPagesFlowByLinkHeader(getCommitDiffsURI(glProject, commit.sha)) { + val commitWithParents = api.rest.loadCommit(projectId, commit.sha).body()!! + val patches = ApiPageUtil.createPagesFlowByLinkHeader(api.rest.getCommitDiffsURI(projectId, commit.sha)) { api.rest.loadCommitDiffs(it) }.map { it.body() }.foldToList(GitLabDiffDTO::toPatch) GitCommitShaWithPatches(commit.sha, commitWithParents.parentIds, patches) @@ -136,14 +140,14 @@ class GitLabMergeRequestChangesImpl( } val headPatches = withContext(Dispatchers.IO) { if (api.getMetadata().version < GitLabVersion(15, 7)) { - ApiPageUtil.createPagesFlowByLinkHeader(api.getMergeRequestChangesURI(glProject, mergeRequestDetails.iid)) { + ApiPageUtil.createPagesFlowByLinkHeader(api.rest.getMergeRequestChangesURI(projectId, mergeRequestDetails.iid)) { api.rest.loadMergeRequestChanges(it) }.map { it.body().changes }.foldToList(GitLabDiffDTO::toPatch) } else { // doesn't send back Link headers... ApiPageUtil.createPagesFlowByPagination { page -> - api.rest.loadMergeRequestDiffs(api.getMergeRequestDiffsURI(glProject, mergeRequestDetails.iid, page)) + api.rest.loadMergeRequestDiffs(api.rest.getMergeRequestDiffsURI(projectId, mergeRequestDetails.iid, page)) }.map { it.body() }.foldToList(GitLabDiffDTO::toPatch) } }.apply { @@ -153,7 +157,7 @@ class GitLabMergeRequestChangesImpl( } } } - return GitBranchComparisonResult.create(repository.project, repository.root, baseSha, mergeBaseSha, commitsWithPatches, headPatches) + return GitBranchComparisonResult.create(project, gitRemoteUrlCoordinates.repository.root, baseSha, mergeBaseSha, commitsWithPatches, headPatches) } override suspend fun ensureAllRevisionsFetched() { @@ -162,12 +166,15 @@ class GitLabMergeRequestChangesImpl( revsToCheck.add(it) } - if (GitCodeReviewUtils.testRevisionsExist(projectMapping.gitRepository, revsToCheck)) return + if (GitCodeReviewUtils.testRevisionsExist(gitRemoteUrlCoordinates.repository, revsToCheck)) return - GitCodeReviewUtils.fetch(projectMapping.gitRepository, projectMapping.gitRemote, mergeRequestDetails.targetBranch) - GitCodeReviewUtils.fetch(projectMapping.gitRepository, projectMapping.gitRemote, """merge-requests/${mergeRequestDetails.iid}/head:""") + GitCodeReviewUtils.fetch(gitRemoteUrlCoordinates.repository, gitRemoteUrlCoordinates.remote, mergeRequestDetails.targetBranch) + GitCodeReviewUtils.fetch(gitRemoteUrlCoordinates.repository, gitRemoteUrlCoordinates.remote, + """merge-requests/${mergeRequestDetails.iid}/head:""") - check(GitCodeReviewUtils.testRevisionsExist(projectMapping.gitRepository, revsToCheck)) { "Failed to fetch some revisions" } + check(GitCodeReviewUtils.testRevisionsExist(gitRemoteUrlCoordinates.repository, revsToCheck)) { + "Failed to fetch some revisions" + } } } diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestDiscussionsContainer.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestDiscussionsContainer.kt index d8e99893a90d..24e90c7791d0 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestDiscussionsContainer.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabMergeRequestDiscussionsContainer.kt @@ -27,7 +27,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabId -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata import org.jetbrains.plugins.gitlab.api.GitLabVersion import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionRestDTO @@ -73,7 +72,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( private val project: Project, private val api: GitLabApi, private val glMetadata: GitLabServerMetadata?, - private val glProject: GitLabProjectCoordinates, + private val projectId: String, private val currentUser: GitLabUserDTO, private val mr: GitLabMergeRequest, ) : GitLabMergeRequestDiscussionsContainer { @@ -96,7 +95,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( private val nonEmptyDiscussionsData: SharedFlow>> by lazy { startGitLabRestETagListLoaderIn( cs, - getMergeRequestDiscussionsUri(glProject, mr.iid), + api.rest.getMergeRequestDiscussionsUri(projectId, mr.iid), { it.id }, requestReloadFlow = reloadRequests, @@ -121,7 +120,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( GitLabDiscussionRestDTO::id, { disc -> LoadedGitLabDiscussion(this, - api, glMetadata, glProject, currentUser, + api, glMetadata, projectId, currentUser, { discussionEvents.emit(it) }, { draftNotesEvents.emit(it) }, mr, disc, getDiscussionDraftNotes(disc.id).throwFailure()) }, @@ -155,7 +154,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( else { startGitLabRestETagListLoaderIn( cs, - getMergeRequestDraftNotesUri(glProject, mr.iid), + api.rest.getMergeRequestDraftNotesUri(projectId, mr.iid), { it.id }, requestReloadFlow = reloadRequests, @@ -182,7 +181,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( mapDataToModel( GitLabMergeRequestDraftNoteRestDTO::id, { - GitLabMergeRequestDraftNoteImpl(this, api, glMetadata, glProject, mr, { draftNotesEvents.emit(it) }, it, currentUser) + GitLabMergeRequestDraftNoteImpl(this, api, glMetadata, projectId, mr, { draftNotesEvents.emit(it) }, it, currentUser) }, { update(it) } ) @@ -204,7 +203,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( override suspend fun addNote(body: String) { withContext(cs.coroutineContext) { val newDiscussion = withContext(Dispatchers.IO) { - api.rest.addNote(glProject, mr.iid, body).body() + api.rest.addNote(projectId, mr.iid, body).body() } withContext(NonCancellable) { @@ -216,7 +215,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( override suspend fun addNote(position: GitLabMergeRequestNewDiscussionPosition, body: String) { withContext(cs.coroutineContext) { val newDiscussion = withContext(Dispatchers.IO) { - api.rest.addDiffNote(glProject, mr.iid, GitLabDiffPositionInput.from(position), body).body() + api.rest.addDiffNote(projectId, mr.iid, GitLabDiffPositionInput.from(position), body).body() } withContext(NonCancellable) { @@ -228,7 +227,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( override suspend fun addDraftNote(body: String) { withContext(cs.coroutineContext) { val newNote = withContext(Dispatchers.IO) { - api.rest.addDraftNote(glProject, mr.iid, null, body).body() + api.rest.addDraftNote(projectId, mr.iid, null, body).body() } withContext(NonCancellable) { @@ -240,7 +239,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( override suspend fun addDraftNote(position: GitLabMergeRequestNewDiscussionPosition, body: String) { withContext(cs.coroutineContext) { val newNote = withContext(Dispatchers.IO) { - api.rest.addDraftNote(glProject, mr.iid, GitLabDiffPositionInput.from(position), body).body() + api.rest.addDraftNote(projectId, mr.iid, GitLabDiffPositionInput.from(position), body).body() } withContext(NonCancellable) { @@ -257,7 +256,7 @@ class GitLabMergeRequestDiscussionsContainerImpl( } withContext(Dispatchers.IO) { - api.rest.submitDraftNotes(glProject, mr.iid) + api.rest.submitDraftNotes(projectId, mr.iid) } withContext(NonCancellable) { draftNotesEvents.emit(AllDeleted()) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabNote.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabNote.kt index f6f24d486439..035e571e964d 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabNote.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabNote.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabId -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabRestId import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata import org.jetbrains.plugins.gitlab.api.GitLabVersion @@ -111,7 +110,7 @@ interface GitLabMergeRequestDraftNote : GitLabMergeRequestNote, MutableGitLabNot class MutableGitLabMergeRequestNote( parentCs: CoroutineScope, private val api: GitLabApi, - private val project: GitLabProjectCoordinates, + private val projectId: String, private val mr: GitLabMergeRequest, private val discussionId: GitLabRestId, private val currentUser: GitLabUserDTO, @@ -153,7 +152,7 @@ class MutableGitLabMergeRequestNote( withContext(cs.coroutineContext) { operationsGuard.withLock { withContext(Dispatchers.IO) { - api.rest.updateNote(project, mr.iid, discussionId.restId, id.restId, newText).body() + api.rest.updateNote(projectId, mr.iid, discussionId.restId, id.restId, newText).body() } } data.update { it.copy(body = newText) } @@ -164,7 +163,7 @@ class MutableGitLabMergeRequestNote( withContext(cs.coroutineContext) { operationsGuard.withLock { withContext(Dispatchers.IO) { - api.rest.deleteNote(project, mr.iid, discussionId.restId, id.restId).body() + api.rest.deleteNote(projectId, mr.iid, discussionId.restId, id.restId).body() } } eventSink(GitLabNoteEvent.Deleted(id)) @@ -183,13 +182,13 @@ class MutableGitLabMergeRequestNote( } withContext(Dispatchers.IO) { if (noteEmoji == null) { - val awardedEmojiDTO = api.rest.addAwardEmoji(project, mr.iid, id.restId, reaction.name).body() + val awardedEmojiDTO = api.rest.addAwardEmoji(projectId, mr.iid, id.restId, reaction.name).body() val awardEmoji = GitLabAwardEmoji.fromDto(awardedEmojiDTO, emojiMap) updateEmojisLocally(awardEmoji) } else { val awardId = noteEmoji.id.restId ?: return@withContext - api.rest.deleteAwardEmoji(project, mr.iid, id.restId, awardId).body() + api.rest.deleteAwardEmoji(projectId, mr.iid, id.restId, awardId).body() updateEmojisLocally(noteEmoji) } } @@ -213,7 +212,7 @@ class MutableGitLabMergeRequestNote( private suspend fun loadEmojis() { runCatching { val emojiMap = emojiMapDeferred.await() - val uri = getMRNotesAwardEmojiUri(project, mr.iid, id.restId) + val uri = api.rest.getMRNotesAwardEmojiUri(projectId, mr.iid, id.restId) val emojis: List = withContext(Dispatchers.IO) { ApiPageUtil.createPagesFlowByLinkHeader(uri) { pageUri -> @@ -241,7 +240,7 @@ class GitLabMergeRequestDraftNoteImpl( parentCs: CoroutineScope, private val api: GitLabApi, private val glMetadata: GitLabServerMetadata?, - private val project: GitLabProjectCoordinates, + private val projectId: String, private val mr: GitLabMergeRequest, private val eventSink: suspend (Change) -> Unit, private val noteData: GitLabMergeRequestDraftNoteRestDTO, @@ -274,7 +273,7 @@ class GitLabMergeRequestDraftNoteImpl( operationsGuard.withLock { withContext(Dispatchers.IO) { // Checked by canEdit - api.rest.updateDraftNote(project, mr.iid, noteData.id.restId, noteData.position, newText) + api.rest.updateDraftNote(projectId, mr.iid, noteData.id.restId, noteData.position, newText) } } data.update { it.copy(note = newText) } @@ -287,7 +286,7 @@ class GitLabMergeRequestDraftNoteImpl( withContext(Dispatchers.IO) { // Shouldn't require extra check, delete and get draft notes was introduced in // the same update - api.rest.deleteDraftNote(project, mr.iid, noteData.id.restId.toLong()).body() + api.rest.deleteDraftNote(projectId, mr.iid, noteData.id.restId.toLong()).body() } } eventSink(Deleted { it.id == id }) @@ -300,7 +299,7 @@ class GitLabMergeRequestDraftNoteImpl( withContext(Dispatchers.IO) { // Shouldn't require extra check, delete and get draft notes was introduced in // the same update - api.rest.submitSingleDraftNote(project, mr.iid, noteData.id.restId.toLong()).body() + api.rest.submitSingleDraftNote(projectId, mr.iid, noteData.id.restId.toLong()).body() } } mr.refreshData() diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProject.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProject.kt index 788ed0c45c51..b5b33f9d3931 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProject.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProject.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project import com.intellij.platform.util.coroutines.childScope import com.intellij.util.containers.nullize +import git4idea.remote.GitRemoteUrlCoordinates import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart @@ -25,7 +26,6 @@ import kotlinx.coroutines.flow.transformWhile import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabGidData import org.jetbrains.plugins.gitlab.api.GitLabGraphQLMutationException -import org.jetbrains.plugins.gitlab.api.GitLabId import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.GitLabServerMetadata import org.jetbrains.plugins.gitlab.api.GitLabVersion @@ -43,7 +43,6 @@ import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestDTO import org.jetbrains.plugins.gitlab.mergerequest.api.request.createMergeRequest import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequest import org.jetbrains.plugins.gitlab.upload.markdownUploadFile -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import org.jetbrains.plugins.gitlab.util.GitLabRegistry import org.jetbrains.plugins.gitlab.util.GitLabStatistics import java.awt.image.BufferedImage @@ -58,7 +57,9 @@ private val LOG = logger() @CodeReviewDomainEntity interface GitLabProject { - val projectMapping: GitLabProjectMapping + val projectCoordinates: GitLabProjectCoordinates + val gitRemote: GitRemoteUrlCoordinates + val projectId: String val dataReloadSignal: SharedFlow val mergeRequests: GitLabProjectMergeRequestsStore @@ -69,7 +70,6 @@ interface GitLabProject { fun getMembersBatches(): Flow> val defaultBranch: String? - val gitLabProjectId: GitLabId suspend fun isMultipleAssigneesAllowed(): Boolean suspend fun isMultipleReviewersAllowed(): Boolean @@ -106,15 +106,16 @@ class GitLabLazyProject( parentCs: CoroutineScope, private val api: GitLabApi, private val glMetadata: GitLabServerMetadata?, - override val projectMapping: GitLabProjectMapping, private val initialData: GitLabProjectDTO, private val currentUser: GitLabUserDTO, private val tokenRefreshFlow: Flow, + override val projectCoordinates: GitLabProjectCoordinates, + override val gitRemote: GitRemoteUrlCoordinates, ) : GitLabProject { private val cs = parentCs.childScope(javaClass.name) - private val projectCoordinates: GitLabProjectCoordinates = projectMapping.repository + override val projectId: String = initialData.id.guessRestId() private val _dataReloadSignal = MutableSharedFlow(replay = 1) override val dataReloadSignal: SharedFlow = _dataReloadSignal.asSharedFlow() @@ -126,20 +127,19 @@ class GitLabLazyProject( loadMultipleAssigneesAllowedFallback() } - override val gitLabProjectId: GitLabId = initialData.id - - override val mergeRequests by lazy { - CachingGitLabProjectMergeRequestsStore(project, cs, api, glMetadata, projectMapping, currentUser, tokenRefreshFlow) + override val mergeRequests: GitLabProjectMergeRequestsStore by lazy { + CachingGitLabProjectMergeRequestsStore(project, cs, api, glMetadata, currentUser, + tokenRefreshFlow, projectCoordinates, projectId, gitRemote) } private val labelsLoader = BatchesLoader(cs, - api.graphQL.createAllProjectLabelsFlow(projectMapping.repository).map { labels -> + api.graphQL.createAllProjectLabelsFlow(projectCoordinates.projectPath).map { labels -> labels.map { GitLabLabel(it.title, it.color) } }) private val membersLoader = BatchesLoader(cs, - ApiPageUtil.createPagesFlowByLinkHeader(getProjectUsersURI(projectMapping.repository)) { + ApiPageUtil.createPagesFlowByLinkHeader(api.rest.getProjectUsersURI(projectId)) { api.rest.getProjectUsers(it) }.map { response -> response.body().map(GitLabUserDTO::fromRestDTO) }) @@ -189,7 +189,7 @@ class GitLabLazyProject( val assigneeIds = assignees.nullize()?.map { GitLabGidData(it.id).guessRestId() } val labelTitles = labels.nullize()?.map { it.title } val iid = api.rest.createMergeRequest( - projectCoordinates, + projectId, sourceBranch, targetBranch, title, @@ -200,7 +200,7 @@ class GitLabLazyProject( ).body().iid val attempts = GitLabRegistry.getRequestPollingAttempts() repeat(attempts) { - val data = api.graphQL.loadMergeRequest(projectCoordinates, iid).body() + val data = api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body() if (data?.diffRefs != null) { return@async data } @@ -221,7 +221,7 @@ class GitLabLazyProject( val filename = path.fileName.toString() val mimeType = Files.probeContentType(path) ?: "application/octet-stream" Files.newInputStream(path).use { - api.rest.markdownUploadFile(projectCoordinates, filename, mimeType, it).body() + api.rest.markdownUploadFile(projectId, filename, mimeType, it).body() } }.await() GitLabStatistics.logFileUploadActionExecuted(project) @@ -235,7 +235,7 @@ class GitLabLazyProject( outputStream.toByteArray() } ByteArrayInputStream(byteArray).use { - api.rest.markdownUploadFile(projectCoordinates, "image.png", "image/png", it).body() + api.rest.markdownUploadFile(projectId, "image.png", "image/png", it).body() } }.await() GitLabStatistics.logFileUploadActionExecuted(project) @@ -247,10 +247,10 @@ class GitLabLazyProject( } private suspend fun getAllowsMultipleAssigneesPropertyFromNamespacePlan() = try { - api.rest.getProjectNamespace(projectMapping.repository.projectPath.owner).body()?.plan?.let { + api.rest.getProjectNamespace(projectCoordinates.projectPath.owner).body()?.plan?.let { it != GitLabPlan.FREE } ?: run { - LOG.warn("Failed to find namespace for project ${projectMapping.repository}") + LOG.warn("Failed to find namespace for project ${projectCoordinates.projectPath.fullPath()}") null } } @@ -258,13 +258,13 @@ class GitLabLazyProject( throw ce } catch (e: Exception) { - LOG.warn("Failed to load namespace for project ${projectMapping.repository}", e) + LOG.warn("Failed to load namespace for project ${projectCoordinates.projectPath.fullPath()}", e) null } private suspend fun getAllowsMultipleAssigneesPropertyFromIssueWidget(): Boolean { val widgetAssignees: WorkItemWidgetAssignees? = try { - api.graphQL.createAllWorkItemsFlow(projectMapping.repository) + api.graphQL.createAllWorkItemsFlow(projectCoordinates.projectPath) .transformWhile { workItems -> val widget = workItems.find { workItem -> workItem.workItemType.name == WorkItemType.ISSUE_TYPE } ?.widgets @@ -285,7 +285,7 @@ class GitLabLazyProject( throw ce } catch (e: Exception) { - LOG.warn("Failed to load work item widgets for project ${projectMapping.repository}", e) + LOG.warn("Failed to load work item widgets for project ${projectCoordinates.projectPath}", e) return false } return widgetAssignees?.allowsMultipleAssignees ?: false diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProjectMergeRequestsStore.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProjectMergeRequestsStore.kt index 652650e8ab21..e3696500c1ec 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProjectMergeRequestsStore.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/data/GitLabProjectMergeRequestsStore.kt @@ -12,6 +12,7 @@ import com.intellij.collaboration.messages.CollaborationToolsBundle import com.intellij.collaboration.util.ResultUtil.runCatchingUser import com.intellij.openapi.project.Project import com.intellij.openapi.util.registry.Registry +import git4idea.remote.GitRemoteUrlCoordinates import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,7 +45,6 @@ import org.jetbrains.plugins.gitlab.mergerequest.api.request.getMergeRequestList import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequest import org.jetbrains.plugins.gitlab.mergerequest.data.loaders.startGitLabRestETagListLoaderIn import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import java.util.concurrent.ConcurrentHashMap interface GitLabProjectMergeRequestsStore { @@ -75,18 +75,20 @@ interface GitLabProjectMergeRequestsStore { targetBranchName: String? = null): List } -class CachingGitLabProjectMergeRequestsStore(private val project: Project, - parentCs: CoroutineScope, - private val api: GitLabApi, - private val glMetadata: GitLabServerMetadata?, - private val projectMapping: GitLabProjectMapping, - private val currentUser: GitLabUserDTO, - private val tokenRefreshFlow: Flow) : GitLabProjectMergeRequestsStore { +class CachingGitLabProjectMergeRequestsStore( + private val project: Project, + parentCs: CoroutineScope, + private val api: GitLabApi, + private val glMetadata: GitLabServerMetadata?, + private val currentUser: GitLabUserDTO, + private val tokenRefreshFlow: Flow, + private val projectCoordinates: GitLabProjectCoordinates, + private val projectId: String, + private val gitRemote: GitRemoteUrlCoordinates, +) : GitLabProjectMergeRequestsStore { private val cs = parentCs.childScope(this::class) - private val glProject: GitLabProjectCoordinates = projectMapping.repository - private val detailsCache = Caffeine.newBuilder() .weakValues() .build() @@ -99,7 +101,7 @@ class CachingGitLabProjectMergeRequestsStore(private val project: Project, override fun getListLoaderIn(cs: CoroutineScope, searchQuery: String): ReloadablePotentiallyInfiniteListLoader { val loader = startGitLabRestETagListLoaderIn( cs, - getMergeRequestListURI(glProject, searchQuery), + api.rest.getMergeRequestListURI(projectId, searchQuery), { it.id }, requestReloadFlow = tokenRefreshFlow.withInitial(Unit), @@ -143,7 +145,15 @@ class CachingGitLabProjectMergeRequestsStore(private val project: Project, .withInitial(iid) .map { mrId -> runCatchingUser { loadMergeRequest(mrId) } } // TODO: create from cached details .transformConsecutiveSuccesses { - mapScoped { mrData -> LoadedGitLabMergeRequest(project, this, api, glMetadata, projectMapping, currentUser, mrData) } + mapScoped { mrData -> LoadedGitLabMergeRequest(project, + this, + api, + glMetadata, + projectCoordinates, + projectId, + gitRemote, + currentUser, + mrData) } } .shareIn(cs, SharingStarted.WhileSubscribed(0, 1000), 1) // this the model will only be alive while it's needed @@ -154,7 +164,7 @@ class CachingGitLabProjectMergeRequestsStore(private val project: Project, sourceBranchName: String, targetBranchName: String?): List = withContext(Dispatchers.IO) { - val body = api.graphQL.findMergeRequestsByBranch(projectMapping.repository, state, sourceBranchName, targetBranchName).body() + val body = api.graphQL.findMergeRequestsByBranch(projectCoordinates.projectPath, state, sourceBranchName, targetBranchName).body() body!!.nodes } @@ -167,7 +177,7 @@ class CachingGitLabProjectMergeRequestsStore(private val project: Project, @Throws(HttpStatusErrorException::class, IllegalStateException::class) private suspend fun loadMergeRequest(iid: String): GitLabMergeRequestDTO { return withContext(Dispatchers.IO) { - val body = api.graphQL.loadMergeRequest(glProject, iid).body() + val body = api.graphQL.loadMergeRequest(projectCoordinates.projectPath, iid).body() if (body == null) { api.rest.getCurrentUser() // Exception is generated automatically if status code >= 400 error(CollaborationToolsBundle.message("graphql.errors", "empty response")) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/GitLabMergeRequestViewModels.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/GitLabMergeRequestViewModels.kt index cd4405e019ce..9513acb02043 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/GitLabMergeRequestViewModels.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/GitLabMergeRequestViewModels.kt @@ -49,10 +49,14 @@ internal class GitLabMergeRequestViewModels( private val openMergeRequestTimeline: (String, Boolean) -> Unit, private val openMergeRequestDiff: (String, Boolean) -> Unit, ) { - private val htmlConverter: GitLabMarkdownToHtmlConverter = GitLabMarkdownToHtmlConverter(project, - projectData.projectMapping.gitRepository, - projectData.projectMapping.repository, - projectData.gitLabProjectId) + private val htmlConverter: GitLabMarkdownToHtmlConverter = + GitLabMarkdownToHtmlConverter( + project, + projectData.gitRemote.repository, + projectData.projectCoordinates.serverPath, + projectData.projectId, + projectData.projectCoordinates.projectPath + ) private val cs = parentCs.childScope(javaClass.name) @@ -82,7 +86,9 @@ internal class GitLabMergeRequestViewModels( val diffVm: GitLabMergeRequestDiffViewModel get() = _diffVm val editorReviewVm: GitLabMergeRequestEditorReviewViewModel by lazy { - GitLabMergeRequestEditorReviewViewModel(cs, project, projectData.projectMapping, currentUser, mergeRequest, + GitLabMergeRequestEditorReviewViewModel(cs, project, + projectData.gitRemote, projectData.projectCoordinates, + currentUser, mergeRequest, discussionsVms, avatarIconProvider, imageLoader, openMergeRequestDetails, openMergeRequestDiff).apply { setup() diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/GitLabMergeRequestCreateComponentFactory.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/GitLabMergeRequestCreateComponentFactory.kt index 94ff53c898fd..cd0d86cc26ed 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/GitLabMergeRequestCreateComponentFactory.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/GitLabMergeRequestCreateComponentFactory.kt @@ -32,7 +32,7 @@ import javax.swing.JComponent internal object GitLabMergeRequestCreateComponentFactory { fun create(project: Project, cs: CoroutineScope, createVm: GitLabMergeRequestCreateViewModel): JComponent { - val directionModel = GitLabMergeRequestCreateDirectionModel(createVm.projectsManager, createVm.projectData.projectMapping) + val directionModel = GitLabMergeRequestCreateDirectionModel(createVm) cs.launchNow { createVm.branchState.filterNotNull().collect { branchState -> directionModel.baseBranch = branchState.baseBranch diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateDirectionModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateDirectionModel.kt index 05f5ec58e5c6..6a90eaf16dee 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateDirectionModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateDirectionModel.kt @@ -5,15 +5,13 @@ import com.intellij.collaboration.ui.SimpleEventListener import com.intellij.util.EventDispatcher import git4idea.GitBranch import git4idea.GitRemoteBranch -import git4idea.remote.hosting.knownRepositories import git4idea.ui.branch.MergeDirectionModel -import org.jetbrains.plugins.gitlab.GitLabProjectsManager import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping internal class GitLabMergeRequestCreateDirectionModel( - private val repositoriesManager: GitLabProjectsManager, - override val baseRepo: GitLabProjectMapping + private val createVm: GitLabMergeRequestCreateViewModel ) : MergeDirectionModel { + override val baseRepo: GitLabProjectMapping = createVm.projectMapping private val changeEventDispatcher = EventDispatcher.create(SimpleEventListener::class.java) override var baseBranch: GitRemoteBranch? = null @@ -37,7 +35,7 @@ internal class GitLabMergeRequestCreateDirectionModel( } override fun getKnownRepoMappings(): List { - return repositoriesManager.knownRepositories.toList() + return createVm.getAllKnownProjects() } override fun setHead(repo: GitLabProjectMapping?, branch: GitBranch?) { diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateViewModel.kt index c8a92fecf954..d8c82c83ba1a 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/create/model/GitLabMergeRequestCreateViewModel.kt @@ -28,6 +28,7 @@ import git4idea.GitRemoteBranch import git4idea.config.GitSharedSettings import git4idea.history.GitLogUtil import git4idea.remote.hosting.changesSignalFlow +import git4idea.remote.hosting.knownRepositories import git4idea.repo.GitRepository import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope @@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy - import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest @@ -61,8 +61,7 @@ import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import org.jetbrains.plugins.gitlab.util.GitLabStatistics internal interface GitLabMergeRequestCreateViewModel : CodeReviewTitleDescriptionViewModel { - val projectsManager: GitLabProjectsManager - val projectData: GitLabProject + val projectMapping: GitLabProjectMapping val avatarIconProvider: IconsProvider val titleGenerationVm: StateFlow @@ -91,6 +90,8 @@ internal interface GitLabMergeRequestCreateViewModel : CodeReviewTitleDescriptio fun updateBranchState(state: BranchState?) + fun getAllKnownProjects(): List + fun setReviewers(reviewers: List) fun setAssignees(assignees: List) fun setLabels(labels: List) @@ -102,8 +103,8 @@ internal interface GitLabMergeRequestCreateViewModel : CodeReviewTitleDescriptio internal class GitLabMergeRequestCreateViewModelImpl( private val project: Project, parentCs: CoroutineScope, - override val projectsManager: GitLabProjectsManager, - override val projectData: GitLabProject, + private val projectsManager: GitLabProjectsManager, + private val projectData: GitLabProject, override val avatarIconProvider: IconsProvider, override val openReviewTabAction: suspend (mrIid: String) -> Unit, private val onReviewCreated: () -> Unit, @@ -158,7 +159,9 @@ internal class GitLabMergeRequestCreateViewModelImpl( }?.iid } - private val gitRepository: GitRepository = projectData.projectMapping.gitRepository + private val gitRepository: GitRepository = projectData.gitRemote.repository + override val projectMapping: GitLabProjectMapping = GitLabProjectMapping(projectData.projectCoordinates, projectData.gitRemote) + private val commitRevisionComparisonFlow: Flow = combine(gitRepository.changesSignalFlow().withInitial(Unit), branchState) { _, state -> state } .map { state -> @@ -240,7 +243,7 @@ internal class GitLabMergeRequestCreateViewModelImpl( init { cs.launch { - val baseRepo = projectData.projectMapping + val baseRepo = GitLabProjectMapping(projectData.projectCoordinates, projectData.gitRemote) val baseGitRepo = baseRepo.gitRepository val defaultBranch = projectData.defaultBranch ?: return@launch val baseBranch = baseGitRepo.getBranchTrackInfo(defaultBranch)?.remoteBranch ?: return@launch @@ -277,6 +280,8 @@ internal class GitLabMergeRequestCreateViewModelImpl( _branchState.value = state } + override fun getAllKnownProjects(): List = projectsManager.knownRepositories.toList() + override fun setReviewers(reviewers: List) { _reviewers.value = reviewers GitLabStatistics.logMrCreationReviewersAdjusted(project) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestBranchesViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestBranchesViewModel.kt index e53bf2858453..1484b4dacf7c 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestBranchesViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestBranchesViewModel.kt @@ -9,9 +9,9 @@ import com.intellij.collaboration.ui.codereview.details.model.CodeReviewBranches import com.intellij.collaboration.ui.codereview.details.model.CodeReviewBranchesViewModel import com.intellij.dvcs.DvcsUtil import com.intellij.openapi.diagnostic.thisLogger +import git4idea.remote.GitRemoteUrlCoordinates import git4idea.remote.hosting.GitRemoteBranchesUtil import git4idea.remote.hosting.changesSignalFlow -import git4idea.repo.GitRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -19,20 +19,20 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import org.jetbrains.plugins.gitlab.api.GitLabServerPath import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequest import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestFullDetails import org.jetbrains.plugins.gitlab.mergerequest.data.getSourceRemoteDescriptor import org.jetbrains.plugins.gitlab.mergerequest.data.getSpecialRemoteBranchForHead import org.jetbrains.plugins.gitlab.mergerequest.util.GitLabMergeRequestBranchUtil -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import org.jetbrains.plugins.gitlab.util.GitLabStatistics internal class GitLabMergeRequestBranchesViewModel( parentCs: CoroutineScope, private val mergeRequest: GitLabMergeRequest, - private val mapping: GitLabProjectMapping + private val serverPath: GitLabServerPath, + private val gitRemote: GitRemoteUrlCoordinates, ) : CodeReviewBranchesViewModel { - private val gitRepository: GitRepository = mapping.remote.repository private val cs: CoroutineScope = parentCs.childScope(this::class) @@ -48,15 +48,15 @@ internal class GitLabMergeRequestBranchesViewModel( return "$sourceProjectOwner:${details.sourceBranch}" } - override val isCheckedOut: SharedFlow = gitRepository.changesSignalFlow().withInitial(Unit) + override val isCheckedOut: SharedFlow = gitRemote.repository.changesSignalFlow().withInitial(Unit) .combine(mergeRequest.details) { _, details -> - val sourceRemote = details.getSourceRemoteDescriptor(mapping.repository.serverPath) + val sourceRemote = details.getSourceRemoteDescriptor(serverPath) if (sourceRemote != null) { - GitRemoteBranchesUtil.isRemoteBranchCheckedOut(gitRepository, sourceRemote, details.sourceBranch) + GitRemoteBranchesUtil.isRemoteBranchCheckedOut(gitRemote.repository, sourceRemote, details.sourceBranch) } else { - val specialRef = details.getSpecialRemoteBranchForHead(mapping.gitRemote) - GitRemoteBranchesUtil.isRemoteBranchCheckedOut(gitRepository, specialRef) + val specialRef = details.getSpecialRemoteBranchForHead(gitRemote.remote) + GitRemoteBranchesUtil.isRemoteBranchCheckedOut(gitRemote.repository, specialRef) } }.modelFlow(cs, thisLogger()) @@ -66,18 +66,18 @@ internal class GitLabMergeRequestBranchesViewModel( override fun fetchAndCheckoutRemoteBranch() { cs.launch { val details = mergeRequest.details.first() - GitLabMergeRequestBranchUtil.fetchAndCheckoutBranch(mapping, details) + GitLabMergeRequestBranchUtil.fetchAndCheckoutBranch(gitRemote.repository, serverPath, details) } - GitLabStatistics.logMrActionExecuted(gitRepository.project, GitLabStatistics.MergeRequestAction.BRANCH_CHECKOUT) + GitLabStatistics.logMrActionExecuted(gitRemote.repository.project, GitLabStatistics.MergeRequestAction.BRANCH_CHECKOUT) } override val canShowInLog: Boolean = true override fun fetchAndShowInLog() { cs.launch { val details = mergeRequest.details.first() - GitLabMergeRequestBranchUtil.fetchAndShowRemoteBranchInLog(mapping, details) + GitLabMergeRequestBranchUtil.fetchAndShowRemoteBranchInLog(gitRemote.repository, serverPath, details) } - GitLabStatistics.logMrActionExecuted(gitRepository.project, GitLabStatistics.MergeRequestAction.SHOW_BRANCH_IN_LOG) + GitLabStatistics.logMrActionExecuted(gitRemote.repository.project, GitLabStatistics.MergeRequestAction.SHOW_BRANCH_IN_LOG) } override fun showBranches() { diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestChangeListViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestChangeListViewModel.kt index ef0266bddbf1..192aba2432de 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestChangeListViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestChangeListViewModel.kt @@ -69,11 +69,11 @@ internal class GitLabMergeRequestChangeListViewModelImpl( persistentChangesViewedState.updatesFlow.withInitial(Unit), ) { discPos, draftsPos, _ -> changes.associateWith { change -> - val sha = parsedChanges.findLatestCommitWithChangesTo(mergeRequest.gitRepository, change.filePath) + val sha = parsedChanges.findLatestCommitWithChangesTo(mergeRequest.gitRemote.repository, change.filePath) val isRead = !isOnLatest || sha?.let { persistentChangesViewedState.isViewed( - mergeRequest.glProject, mergeRequest.iid, - mergeRequest.gitRepository, + mergeRequest.serverPath, mergeRequest.projectId, mergeRequest.iid, + mergeRequest.gitRemote.repository, change.filePath, it ) } ?: false @@ -104,13 +104,13 @@ internal class GitLabMergeRequestChangeListViewModelImpl( override fun setViewedState(changes: Iterable, viewed: Boolean) { val filePathsWithShas = changes.mapNotNull { change -> val path = change.filePath - parsedChanges.findLatestCommitWithChangesTo(mergeRequest.gitRepository, path)?.let { + parsedChanges.findLatestCommitWithChangesTo(mergeRequest.gitRemote.repository, path)?.let { path to it } } persistentChangesViewedState.markViewed( - mergeRequest.glProject, mergeRequest.iid, - mergeRequest.gitRepository, + mergeRequest.serverPath, mergeRequest.projectId, mergeRequest.iid, + mergeRequest.gitRemote.repository, filePathsWithShas, viewed ) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestDetailsViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestDetailsViewModel.kt index 12f7162a809e..14c73a5813a4 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestDetailsViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabMergeRequestDetailsViewModel.kt @@ -50,7 +50,7 @@ internal class GitLabMergeRequestDetailsViewModelImpl( currentUser: GitLabUserDTO, projectData: GitLabProject, private val mergeRequest: GitLabMergeRequest, - private val avatarIconsProvider: IconsProvider, + avatarIconsProvider: IconsProvider, htmlConverter: GitLabMarkdownToHtmlConverter, ) : GitLabMergeRequestDetailsViewModel { @@ -86,9 +86,19 @@ internal class GitLabMergeRequestDetailsViewModelImpl( override val detailsReviewFlowVm = GitLabMergeRequestReviewFlowViewModelImpl( project, cs, currentUser, projectData, mergeRequest, avatarIconsProvider ) - override val branchesVm = GitLabMergeRequestBranchesViewModel(cs, mergeRequest, projectData.projectMapping) - override val statusVm = GitLabMergeRequestStatusViewModelImpl(project, cs, projectData.projectMapping.gitRepository, - projectData.projectMapping.repository.serverPath, mergeRequest) + override val branchesVm = GitLabMergeRequestBranchesViewModel( + cs, + mergeRequest, + projectData.projectCoordinates.serverPath, + projectData.gitRemote + ) + override val statusVm = GitLabMergeRequestStatusViewModelImpl( + project, + cs, + projectData.gitRemote.repository, + projectData.projectCoordinates.serverPath, + mergeRequest + ) override val changesVm = GitLabMergeRequestChangesViewModelImpl(project, cs, mergeRequest, htmlConverter) override fun reloadData() { diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabPersistentMergeRequestChangesViewedState.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabPersistentMergeRequestChangesViewedState.kt index 85c6976f91e1..e608bb958242 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabPersistentMergeRequestChangesViewedState.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/details/model/GitLabPersistentMergeRequestChangesViewedState.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import kotlinx.serialization.Serializable -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates +import org.jetbrains.plugins.gitlab.api.GitLabServerPath import java.time.Duration @Service(Service.Level.PROJECT) @@ -40,7 +40,8 @@ internal class GitLabPersistentMergeRequestChangesViewedState @Serializable data class MRId( - val project: GitLabProjectCoordinates, + val serverPath: GitLabServerPath, + val projectId: String, val iid: String ) @@ -55,22 +56,31 @@ internal class GitLabPersistentMergeRequestChangesViewedState val viewedFiles: Map = mapOf() ) - fun isViewed(glProject: GitLabProjectCoordinates, iid: String, - gitRepository: GitRepository, - filePath: FilePath, sha: String): Boolean { + fun isViewed( + serverPath: GitLabServerPath, + projectId: String, + iid: String, + gitRepository: GitRepository, + filePath: FilePath, + sha: String, + ): Boolean { val relPath = VcsFileUtil.relativePath(gitRepository.root, filePath) - return state.statesMap[MRId(glProject, iid)]?.let { + return state.statesMap[MRId(serverPath, projectId, iid)]?.let { it.viewedFiles[relPath] == sha } ?: false } - fun markViewed(glProject: GitLabProjectCoordinates, iid: String, - gitRepository: GitRepository, - filePathsAndShas: Iterable>, - viewed: Boolean) { + fun markViewed( + serverPath: GitLabServerPath, + projectId: String, + iid: String, + gitRepository: GitRepository, + filePathsAndShas: Iterable>, + viewed: Boolean, + ) { val relPathsAndShas = filePathsAndShas.map { VcsFileUtil.relativePath(gitRepository.root, it.first) to it.second } - updateStateOrCreateAndCleanup(MRId(glProject, iid)) { st -> + updateStateOrCreateAndCleanup(MRId(serverPath, projectId, iid)) { st -> if (viewed) { MRViewedState(id = st.id, viewedFiles = st.viewedFiles + relPathsAndShas) } diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/diff/GitLabMergeRequestDiffReviewViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/diff/GitLabMergeRequestDiffReviewViewModel.kt index 5da630d08a17..e4b778601b48 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/diff/GitLabMergeRequestDiffReviewViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/diff/GitLabMergeRequestDiffReviewViewModel.kt @@ -119,8 +119,8 @@ internal class GitLabMergeRequestDiffReviewViewModelImpl( override fun markViewed() { val sha = mergeRequest.details.value.diffRefs?.headSha ?: return persistentChangesViewedState.markViewed( - mergeRequest.glProject, mergeRequest.iid, - mergeRequest.gitRepository, + mergeRequest.serverPath, mergeRequest.projectId, mergeRequest.iid, + mergeRequest.gitRemote.repository, listOf(change.filePath to sha), true ) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/editor/GitLabMergeRequestEditorReviewViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/editor/GitLabMergeRequestEditorReviewViewModel.kt index 23dfa1050780..b6c21461123f 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/editor/GitLabMergeRequestEditorReviewViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/editor/GitLabMergeRequestEditorReviewViewModel.kt @@ -30,7 +30,9 @@ import com.intellij.util.EventDispatcher import git4idea.branch.GitBranchSyncStatus import git4idea.changes.GitBranchComparisonResult import git4idea.changes.GitTextFilePatchWithHistory +import git4idea.remote.GitRemoteUrlCoordinates import git4idea.remote.hosting.localCommitsSyncStatus +import git4idea.repo.GitRepository import git4idea.ui.branch.GitCurrentBranchPresenter import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope @@ -55,6 +57,7 @@ import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jetbrains.annotations.ApiStatus +import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO import org.jetbrains.plugins.gitlab.data.GitLabImageLoader import org.jetbrains.plugins.gitlab.mergerequest.GitLabMergeRequestsPreferences @@ -63,7 +66,6 @@ import org.jetbrains.plugins.gitlab.mergerequest.ui.createDiffDataFlow import org.jetbrains.plugins.gitlab.mergerequest.ui.review.GitLabMergeRequestDiscussionsViewModels import org.jetbrains.plugins.gitlab.mergerequest.ui.review.GitLabMergeRequestReviewViewModelBase import org.jetbrains.plugins.gitlab.mergerequest.util.GitLabMergeRequestBranchUtil -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping import org.jetbrains.plugins.gitlab.util.GitLabStatistics import java.util.EventListener @@ -73,7 +75,8 @@ private val LOG = logger() class GitLabMergeRequestEditorReviewViewModel internal constructor( parentCs: CoroutineScope, private val project: Project, - private val projectMapping: GitLabProjectMapping, + gitRemote: GitRemoteUrlCoordinates, + private val actualProjectCoordinates: GitLabProjectCoordinates, currentUser: GitLabUserDTO, private val mergeRequest: GitLabMergeRequest, private val discussionsVms: GitLabMergeRequestDiscussionsViewModels, @@ -88,6 +91,8 @@ class GitLabMergeRequestEditorReviewViewModel internal constructor( ), CodeReviewInEditorViewModel { private val preferences = project.service() + private val gitRepository: GitRepository = gitRemote.repository + val mergeRequestIid: String = mergeRequest.iid private val _actualChangesState = MutableStateFlow(ChangesState.NotLoaded) @@ -138,7 +143,7 @@ class GitLabMergeRequestEditorReviewViewModel internal constructor( @OptIn(ExperimentalCoroutinesApi::class) val localRepositorySyncStatus: StateFlow?> by lazy { - val repository = projectMapping.remote.repository + val repository = gitRepository _actualChangesState.map { (it as? ChangesState.Loaded)?.changes?.commits?.map { it.sha } }.distinctUntilChanged().transformLatest { @@ -188,7 +193,7 @@ class GitLabMergeRequestEditorReviewViewModel internal constructor( override fun updateBranch() { cs.launch { val details = mergeRequest.refreshDataNow() - GitLabMergeRequestBranchUtil.fetchAndCheckoutBranch(projectMapping, details) + GitLabMergeRequestBranchUtil.fetchAndCheckoutBranch(gitRepository, actualProjectCoordinates.serverPath, details) } } @@ -238,7 +243,7 @@ class GitLabMergeRequestEditorReviewViewModel internal constructor( */ fun getFileStateFlow(virtualFile: VirtualFile): Flow { if (!virtualFile.isValid || virtualFile.isDirectory || - !VfsUtilCore.isAncestor(projectMapping.remote.repository.root, virtualFile, true)) { + !VfsUtilCore.isAncestor(gitRepository.root, virtualFile, true)) { return flowOf(FileReviewState.NotInReview) } val filePath = VcsContextFactory.getInstance().createFilePathOn(virtualFile) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineDiscussionViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineDiscussionViewModel.kt index f68c7997509d..04ae885d9001 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineDiscussionViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineDiscussionViewModel.kt @@ -81,7 +81,7 @@ class GitLabMergeRequestTimelineDiscussionViewModelImpl( .modelFlow(cs, LOG) override val id: String = discussion.id.toString() - override val serverUrl: URL = mr.glProject.serverPath.toURL() + override val serverUrl: URL = mr.serverPath.toURL() override val author: Flow = mainNote.map { it.author } private val _repliesFolded = MutableStateFlow(true) diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineItemViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineItemViewModel.kt index ba26591c6df4..e6fd5763b295 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineItemViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineItemViewModel.kt @@ -126,7 +126,7 @@ sealed interface GitLabMergeRequestTimelineItemViewModel { override val author: GitLabUserDTO = note.author override val createdAt: Date? = note.createdAt override val isDraft: Boolean = note is GitLabMergeRequestDraftNote - override val serverUrl: URL = mr.glProject.serverPath.toURL() + override val serverUrl: URL = mr.serverPath.toURL() override val actionsVm: GitLabNoteAdminActionsViewModel? = if (note is MutableGitLabNote && note.canAdmin) GitLabNoteAdminActionsViewModelImpl(cs, project, projectData, note) else null diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineViewModel.kt index fbe86839a5c1..01e9ef999f45 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/timeline/GitLabMergeRequestTimelineViewModel.kt @@ -105,7 +105,7 @@ internal class LoadAllGitLabMergeRequestTimelineViewModel( null } - override val serverUrl: URL = mergeRequest.glProject.serverPath.toURL() + override val serverUrl: URL = mergeRequest.serverPath.toURL() private val _diffRequests = MutableSharedFlow() val diffRequests: Flow = _diffRequests.asSharedFlow() diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/toolwindow/model/GitLabReviewTabViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/toolwindow/model/GitLabReviewTabViewModel.kt index cbe483d40fa6..8312657af389 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/toolwindow/model/GitLabReviewTabViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/ui/toolwindow/model/GitLabReviewTabViewModel.kt @@ -45,7 +45,7 @@ internal sealed interface GitLabReviewTabViewModel : ReviewTabViewModel { ) : GitLabReviewTabViewModel, Disposable { private val cs = parentCs.childScope(this::class).cancelledWith(this) - private val projectPath = projectData.projectMapping.repository.projectPath.fullPath() + private val projectPath = projectData.projectCoordinates.projectPath override val displayName: String = GitLabBundle.message("merge.request.create.tab.title", projectPath) val createVm = GitLabMergeRequestCreateViewModelImpl( diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/util/GitLabMergeRequestBranchUtil.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/util/GitLabMergeRequestBranchUtil.kt index 451a14c7f5ac..f21e842aa2a4 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/util/GitLabMergeRequestBranchUtil.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/mergerequest/util/GitLabMergeRequestBranchUtil.kt @@ -3,50 +3,67 @@ package org.jetbrains.plugins.gitlab.mergerequest.util import git4idea.GitRemoteBranch import git4idea.remote.hosting.GitRemoteBranchesUtil +import git4idea.repo.GitRepository +import org.jetbrains.plugins.gitlab.api.GitLabServerPath import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestFullDetails import org.jetbrains.plugins.gitlab.mergerequest.data.getSourceRemoteDescriptor import org.jetbrains.plugins.gitlab.mergerequest.data.getSpecialRemoteBranchForHead import org.jetbrains.plugins.gitlab.mergerequest.data.getTargetRemoteDescriptor import org.jetbrains.plugins.gitlab.mergerequest.data.isFork -import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping object GitLabMergeRequestBranchUtil { private const val FORK_BRANCH_PREFIX = "fork" - private suspend fun findSourceRemoteBranch(mapping: GitLabProjectMapping, details: GitLabMergeRequestFullDetails): GitRemoteBranch? { - val sourceRemoteDescriptor = details.getSourceRemoteDescriptor(mapping.repository.serverPath) + private suspend fun findSourceRemoteBranch( + gitRepository: GitRepository, + serverPath: GitLabServerPath, + details: GitLabMergeRequestFullDetails + ): GitRemoteBranch? { + val sourceRemoteDescriptor = details.getSourceRemoteDescriptor(serverPath) if (sourceRemoteDescriptor != null) { // Public fork / regular branch - return GitRemoteBranchesUtil.findOrCreateRemoteBranch(mapping.gitRepository, sourceRemoteDescriptor, details.sourceBranch) + return GitRemoteBranchesUtil.findOrCreateRemoteBranch(gitRepository, sourceRemoteDescriptor, details.sourceBranch) } else { // Private/deleted fork, can still fetch using special MR head ref - val targetRemoteDescriptor = details.getTargetRemoteDescriptor(mapping.repository.serverPath) - val targetRemote = GitRemoteBranchesUtil.findOrCreateRemote(mapping.gitRepository, targetRemoteDescriptor) ?: return null + val targetRemoteDescriptor = details.getTargetRemoteDescriptor(serverPath) + val targetRemote = GitRemoteBranchesUtil.findOrCreateRemote(gitRepository, targetRemoteDescriptor) ?: return null return details.getSpecialRemoteBranchForHead(targetRemote) } } - private suspend fun findTargetRemoteBranch(mapping: GitLabProjectMapping, details: GitLabMergeRequestFullDetails): GitRemoteBranch? { - val targetRemoteDescriptor = details.getTargetRemoteDescriptor(mapping.repository.serverPath) + private suspend fun findTargetRemoteBranch( + gitRepository: GitRepository, + serverPath: GitLabServerPath, + details: GitLabMergeRequestFullDetails + ): GitRemoteBranch? { + val targetRemoteDescriptor = details.getTargetRemoteDescriptor(serverPath) - return GitRemoteBranchesUtil.findOrCreateRemoteBranch(mapping.gitRepository, targetRemoteDescriptor, details.targetBranch) + return GitRemoteBranchesUtil.findOrCreateRemoteBranch(gitRepository, targetRemoteDescriptor, details.targetBranch) } - suspend fun fetchAndCheckoutBranch(mapping: GitLabProjectMapping, details: GitLabMergeRequestFullDetails) { + suspend fun fetchAndCheckoutBranch( + gitRepository: GitRepository, + serverPath: GitLabServerPath, + details: GitLabMergeRequestFullDetails + ) { val localPrefix = if (details.isFork()) { if (details.sourceProject != null) "${FORK_BRANCH_PREFIX}/${details.sourceProject.ownerPath}" else "${FORK_BRANCH_PREFIX}/${details.author.username}" } else null - val remoteBranch = findSourceRemoteBranch(mapping, details) ?: return - GitRemoteBranchesUtil.fetchAndCheckoutRemoteBranch(mapping.gitRepository, remoteBranch, localPrefix) + val remoteBranch = findSourceRemoteBranch(gitRepository, serverPath, details) ?: return + GitRemoteBranchesUtil.fetchAndCheckoutRemoteBranch(gitRepository, remoteBranch, localPrefix) } - suspend fun fetchAndShowRemoteBranchInLog(mapping: GitLabProjectMapping, details: GitLabMergeRequestFullDetails) { - val sourceRemoteBranch = findSourceRemoteBranch(mapping, details) ?: return - val targetRemoteBranch = findTargetRemoteBranch(mapping, details) + suspend fun fetchAndShowRemoteBranchInLog( + gitRepository: GitRepository, + serverPath: GitLabServerPath, + details: GitLabMergeRequestFullDetails + ) { + val sourceRemoteBranch = findSourceRemoteBranch(gitRepository, serverPath, details) ?: return + val targetRemoteBranch = findTargetRemoteBranch(gitRepository, serverPath, details) - GitRemoteBranchesUtil.fetchAndShowRemoteBranchInLog(mapping.gitRepository, sourceRemoteBranch, targetRemoteBranch) + GitRemoteBranchesUtil.fetchAndShowRemoteBranchInLog(gitRepository, sourceRemoteBranch, targetRemoteBranch) } } \ No newline at end of file diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/notification/GitLabPushNotificationCustomizer.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/notification/GitLabPushNotificationCustomizer.kt index b4134b6c7986..218abd6f082c 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/notification/GitLabPushNotificationCustomizer.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/notification/GitLabPushNotificationCustomizer.kt @@ -22,7 +22,6 @@ import org.jetbrains.plugins.gitlab.GitLabProjectsManager import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabApiManager import org.jetbrains.plugins.gitlab.api.GitLabProjectConnectionManager -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.request.findProject import org.jetbrains.plugins.gitlab.authentication.accounts.GitLabAccount import org.jetbrains.plugins.gitlab.authentication.accounts.GitLabAccountManager @@ -34,6 +33,7 @@ import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestState import org.jetbrains.plugins.gitlab.mergerequest.ui.create.action.GitLabMergeRequestOpenCreateTabNotificationAction import org.jetbrains.plugins.gitlab.mergerequest.ui.create.action.GitLabOpenMergeRequestExistingTabNotificationAction import org.jetbrains.plugins.gitlab.util.GitLabProjectMapping +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath private val LOG = logger() @@ -48,31 +48,53 @@ internal class GitLabPushNotificationCustomizer(private val project: Project) : // If we already have a GitLab connection open, make sure it matches val connection = project.serviceAsync().connectionState.value - if (connection != null && (connection.repo.gitRepository != repository || connection.repo.gitRemote != remoteBranch.remote)) { - return emptyList() - } - val (projectMapping, account) = connection?.let { - it.repo to it.account - } ?: run { - val accountManager = serviceAsync() - val savedAccount = project.serviceAsync().selectedUrlAndAccountId?.second?.let { savedId -> - accountManager.findAccountOrNull { it.id == savedId } + if (connection != null) { + if (connection.repo.gitRepository != repository || connection.repo.gitRemote != remoteBranch.remote) { + return emptyList() } - GitPushNotificationUtil.findRepositoryAndAccount( - project.serviceAsync().knownRepositories, - repository, remoteBranch.remote, - accountManager.accountsState.value, - savedAccount, - project.serviceAsync().account - ) - } ?: return emptyList() + return createActionList(connection.account, connection.projectData.projectCoordinates.projectPath, remoteBranch, connection.repo) + } - if (!canCreateReview(projectMapping, account, remoteBranch)) { + val accountManager = serviceAsync() + val savedAccount = project.serviceAsync().selectedUrlAndAccountId?.second?.let { savedId -> + accountManager.findAccountOrNull { it.id == savedId } + } + val (projectMapping, account) = GitPushNotificationUtil.findRepositoryAndAccount( + project.serviceAsync().knownRepositories, + repository, remoteBranch.remote, + accountManager.accountsState.value, + savedAccount, + project.serviceAsync().account + ) ?: return emptyList() + // be aware, for a renamed project no action will be returned, because of the incorrect path + val projectFullPath = projectMapping.repository.projectPath + return createActionList(account, projectFullPath, remoteBranch, projectMapping) + } + + private suspend fun createActionList( + account: GitLabAccount, + projectFullPath: GitLabProjectPath, + remoteBranch: GitRemoteBranch, + projectMapping: GitLabProjectMapping, + ): List { + val accountManager = serviceAsync() + val token = accountManager.findCredentials(account) ?: return emptyList() + val api = serviceAsync().getClient(account.server, token) + + if (!canCreateReview(api, projectFullPath, remoteBranch)) { return emptyList() } - val existingMRs = findExistingMergeRequests(projectMapping, account, remoteBranch) + val existingMRs = findExistingMergeRequests(api, projectFullPath, remoteBranch) + return actions(existingMRs, projectMapping, account) + } + + private fun actions( + existingMRs: List, + projectMapping: GitLabProjectMapping, + account: GitLabAccount, + ): List { return when (existingMRs.size) { 0 -> { listOf(GitLabMergeRequestOpenCreateTabNotificationAction(project, projectMapping, account)) @@ -93,14 +115,13 @@ internal class GitLabPushNotificationCustomizer(private val project: Project) : * - The repository must exist * - The branch cannot be the default branch (we don't allow creating an MR from default -> default) */ - private suspend fun canCreateReview(projectMapping: GitLabProjectMapping, account: GitLabAccount, branch: GitRemoteBranch): Boolean { - val accountManager = serviceAsync() - val token = accountManager.findCredentials(account) ?: return false - val api = serviceAsync().getClient(account.server, token) - - val repository = projectMapping.repository - val repositoryInfo = getRepositoryInfo(api, repository) ?: run { - LOG.warn("Repository not found: $repository") + private suspend fun canCreateReview( + api: GitLabApi, + projectFullPath: GitLabProjectPath, + branch: GitRemoteBranch, + ): Boolean { + val repositoryInfo = getRepositoryInfo(api, projectFullPath) ?: run { + LOG.warn("Repository not found: $projectFullPath") return false } @@ -109,15 +130,15 @@ internal class GitLabPushNotificationCustomizer(private val project: Project) : return repositoryInfo.rootRef != remoteBranchName } - private suspend fun getRepositoryInfo(api: GitLabApi, project: GitLabProjectCoordinates) = + private suspend fun getRepositoryInfo(api: GitLabApi, projectFullPath: GitLabProjectPath) = try { - api.graphQL.findProject(project).body()?.repository + api.graphQL.findProject(projectFullPath).body()?.repository } catch (ce: CancellationException) { throw ce } catch (e: Exception) { - LOG.warn("Failed to lookup repository $project", e) + LOG.warn("Failed to lookup repository $projectFullPath in $project", e) null } @@ -125,28 +146,22 @@ internal class GitLabPushNotificationCustomizer(private val project: Project) : * Look up any existing open merge requests on the given remote branch. */ private suspend fun findExistingMergeRequests( - projectMapping: GitLabProjectMapping, - account: GitLabAccount, + api: GitLabApi, + targetProjectPath: GitLabProjectPath, branch: GitRemoteBranch, ): List { - val accountManager = serviceAsync() - val token = accountManager.findCredentials(account) ?: return emptyList() - val api = serviceAsync().getClient(account.server, token) - - val repository = projectMapping.repository val remoteBranchName = branch.nameForRemoteOperations return withContext(Dispatchers.IO) { - val targetProjectPath = repository.projectPath.fullPath() try { - val mrs = api.graphQL.findMergeRequestsByBranch(repository, GitLabMergeRequestState.OPENED, remoteBranchName).body()!!.nodes - mrs.filter { it.targetProject.fullPath == targetProjectPath && it.sourceProject?.fullPath == targetProjectPath } + val mrs = api.graphQL.findMergeRequestsByBranch(targetProjectPath, GitLabMergeRequestState.OPENED, remoteBranchName).body()!!.nodes + mrs.filter { it.targetProject.fullPath == targetProjectPath.fullPath() && it.sourceProject?.fullPath == targetProjectPath.fullPath() } } catch (ce: CancellationException) { throw ce } catch (e: Exception) { - LOG.warn("Failed to lookup existing merge requests for branch $remoteBranchName in $repository", e) + LOG.warn("Failed to lookup existing merge requests for branch $remoteBranchName in $targetProjectPath", e) emptyList() } } diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverter.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverter.kt index e259d4670171..96b0ba4329fd 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverter.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverter.kt @@ -44,9 +44,8 @@ import org.intellij.markdown.parser.sequentialparsers.impl.MathParser import org.intellij.markdown.parser.sequentialparsers.impl.ReferenceLinkParser import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.NonNls -import org.jetbrains.plugins.gitlab.api.GitLabId -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.GitLabServerPath +import org.jetbrains.plugins.gitlab.api.projectApiUri import org.jetbrains.plugins.gitlab.util.GitLabProjectPath import java.net.URI import java.nio.file.InvalidPathException @@ -59,8 +58,9 @@ import java.nio.file.InvalidPathException class GitLabMarkdownToHtmlConverter( private val project: Project, private val repository: GitRepository, - projectCoordinates: GitLabProjectCoordinates, - projectId: GitLabId, + serverPath: GitLabServerPath, + projectId: String, + private val projectPath: GitLabProjectPath, ) { companion object { @@ -70,9 +70,8 @@ class GitLabMarkdownToHtmlConverter( internal const val OPEN_MR_LINK_PREFIX = "glmergerequest:" } - private val projectWebUrlBase: String = projectCoordinates.serverPath.toString() + "/-/project/" + projectId.guessRestId() - private val projectApiUri: URI = projectCoordinates.restApiUri - private val projectPath: GitLabProjectPath = projectCoordinates.projectPath + private val projectWebUrlBase: String = "$serverPath/-/project/$projectId" + private val projectApiUri: URI = serverPath.projectApiUri(projectId) /** * Makes file links relative to the git repository root or to the external root. diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabMergeRequestDiscussionViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabMergeRequestDiscussionViewModel.kt index e90f09d1d5fc..6a8367f508e5 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabMergeRequestDiscussionViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabMergeRequestDiscussionViewModel.kt @@ -145,7 +145,7 @@ class GitLabMergeRequestStandaloneDraftNoteViewModelBase internal constructor( override val author: GitLabUserDTO = note.author override val createdAt: Date? = note.createdAt override val isDraft: Boolean = true - override val serverUrl: URL = mr.glProject.serverPath.toURL() + override val serverUrl: URL = mr.serverPath.toURL() override val actionsVm: GitLabNoteAdminActionsViewModel? = if (note.canAdmin) GitLabNoteAdminActionsViewModelImpl(cs, project, projectData, note) else null diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabNoteViewModel.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabNoteViewModel.kt index cbc5c3c9d8d3..ca077708ed2f 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabNoteViewModel.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/ui/comment/GitLabNoteViewModel.kt @@ -65,7 +65,7 @@ class GitLabNoteViewModelImpl( override val author: GitLabUserDTO = note.author override val createdAt: Date? = note.createdAt override val isDraft: Boolean = note is GitLabMergeRequestDraftNote - override val serverUrl: URL = projectData.projectMapping.repository.serverPath.toURL() + override val serverUrl: URL = projectData.projectCoordinates.serverPath.toURL() override val actionsVm: GitLabNoteAdminActionsViewModel? = if (note is MutableGitLabNote && note.canAdmin) GitLabNoteAdminActionsViewModelImpl(cs, project, projectData, note) else null diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/upload/GitLabUploadsApi.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/upload/GitLabUploadsApi.kt index 0386c446377d..21c439213df2 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/upload/GitLabUploadsApi.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/upload/GitLabUploadsApi.kt @@ -4,10 +4,9 @@ package org.jetbrains.plugins.gitlab.upload import com.intellij.collaboration.api.json.loadJsonValue import com.intellij.collaboration.util.resolveRelative import org.jetbrains.plugins.gitlab.api.GitLabApi -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.SinceGitLab import org.jetbrains.plugins.gitlab.api.dto.GitLabUploadRestDTO -import org.jetbrains.plugins.gitlab.api.restApiUri +import org.jetbrains.plugins.gitlab.api.projectApiUrl import java.io.InputStream import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse @@ -16,10 +15,10 @@ import java.util.UUID @SinceGitLab("15.10") internal suspend fun GitLabApi.Rest.markdownUploadFile( - project: GitLabProjectCoordinates, - filename: String, - mimeType: String, - fileInputStream: InputStream, + projectId: String, + filename: String, + mimeType: String, + fileInputStream: InputStream, ): HttpResponse { val boundary = "FormBoundary" + UUID.randomUUID() val boundaryStart = "--$boundary\r\n" + @@ -33,7 +32,7 @@ internal suspend fun GitLabApi.Rest.markdownUploadFile( BodyPublishers.ofString(boundaryEnd) ) - val uri = project.restApiUri.resolveRelative("uploads") + val uri = projectApiUrl(projectId).resolveRelative("uploads") val httpRequest = request(uri) .POST(bodyPublisher) .header("Content-Type", "multipart/form-data; boundary=$boundary") diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/GitLabProjectPath.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/GitLabProjectPath.kt index 997d09a9d082..b43ffe0a9219 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/GitLabProjectPath.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/GitLabProjectPath.kt @@ -3,7 +3,6 @@ package org.jetbrains.plugins.gitlab.util import com.intellij.openapi.util.NlsSafe import com.intellij.util.text.nullize -import git4idea.remote.GitRemoteUrlCoordinates import git4idea.remote.hosting.GitHostingUrlUtil import kotlinx.serialization.Serializable import org.jetbrains.plugins.gitlab.api.GitLabServerPath @@ -17,13 +16,17 @@ data class GitLabProjectPath(val owner: @NlsSafe String, val name: @NlsSafe Stri override fun toString(): String = "$owner/$name" companion object { - fun create(server: GitLabServerPath, remote: GitRemoteUrlCoordinates): GitLabProjectPath? { + fun create(server: GitLabServerPath, gitRemoteUrl: String): GitLabProjectPath? { val serverPath = server.toURI().path - val remotePath = GitHostingUrlUtil.getUriFromRemoteUrl(remote.url)?.path ?: return null + val remotePath = GitHostingUrlUtil.getUriFromRemoteUrl(gitRemoteUrl)?.path ?: return null if (!remotePath.startsWith(serverPath)) return null val repositoryPath = remotePath.removePrefix(serverPath).removePrefix("/") + return extractProjectPath(repositoryPath) + } + + fun extractProjectPath(repositoryPath: String): GitLabProjectPath? { val lastSlashIdx = repositoryPath.lastIndexOf('/') if (lastSlashIdx < 0) return null diff --git a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/gitLabStatistics.kt b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/gitLabStatistics.kt index f2770c5092ba..18dbec8943fa 100644 --- a/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/gitLabStatistics.kt +++ b/plugins/gitlab/gitlab-core/src/org/jetbrains/plugins/gitlab/util/gitLabStatistics.kt @@ -96,7 +96,7 @@ object GitLabStatistics { //endregion //region Counters - private val COUNTERS_GROUP = EventLogGroup("vcs.gitlab.counters", version = 30) + private val COUNTERS_GROUP = EventLogGroup("vcs.gitlab.counters", version = 31) /** * Server metadata was fetched @@ -333,6 +333,7 @@ object GitLabStatistics { enum class GitLabApiRequestName { REST_GET_CURRENT_USER, + REST_GET_PROJECT, REST_GET_PROJECT_IS_FORKED, REST_GET_PROJECT_NAMESPACE, REST_GET_PROJECT_USERS, @@ -446,7 +447,7 @@ internal class GitLabMetricsLoader(private val project: Project) { val nonFork = withContext(Dispatchers.IO) { knownRepos.firstOrNull { repo -> val (api, _) = getApi(repo.repository.serverPath) ?: return@firstOrNull false - !api.isProjectForked(repo.repository) + !api.isProjectForked(repo.repository.projectPath) } } if (nonFork != null) return nonFork @@ -460,7 +461,7 @@ internal class GitLabMetricsLoader(private val project: Project) { val chosenRepoMapping = chooseRepo() ?: return null val (api, account) = getApi(chosenRepoMapping.repository.serverPath) ?: return null - return api.graphQL.getMergeRequestMetrics(chosenRepoMapping.repository, account.name).body() + return api.graphQL.getMergeRequestMetrics(chosenRepoMapping.repository.projectPath, account.name).body() } catch (e: Exception) { LOG.warn("Failed to load metrics", e) diff --git a/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverterTest.kt b/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverterTest.kt index 0b99734caffb..c0e4eb3b9f51 100644 --- a/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverterTest.kt +++ b/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/ui/GitLabMarkdownToHtmlConverterTest.kt @@ -7,16 +7,14 @@ import git4idea.repo.GitRepository import io.mockk.every import io.mockk.mockk import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates -import org.jetbrains.plugins.gitlab.api.GitLabRestIdData import org.jetbrains.plugins.gitlab.api.GitLabServerPath import org.jetbrains.plugins.gitlab.ui.GitLabMarkdownToHtmlConverter.Companion.OPEN_FILE_LINK_PREFIX import org.jetbrains.plugins.gitlab.ui.GitLabMarkdownToHtmlConverter.Companion.OPEN_MR_LINK_PREFIX -import org.jetbrains.plugins.gitlab.util.GitLabProjectPath +import org.jetbrains.plugins.gitlab.util.GitLabProjectPath.Companion.extractProjectPath import java.nio.file.Path -private const val IMAGES_API_BASE = """http://base/url/api/v4/projects/test-account%2Fmr-test""" -private const val WEB_BASE = """http://base/url/-/project/testRestId""" +private const val IMAGES_API_BASE = """http://base/url/api/v4/projects/1""" +private const val WEB_BASE = """http://base/url/-/project/1""" private const val P_CLASS = """class="custom_image"""" class GitLabMarkdownToHtmlConverterTest : LightPlatformTestCase() { @@ -318,10 +316,9 @@ class GitLabMarkdownToHtmlConverterTest : LightPlatformTestCase() { private fun convertToHtml(markdownSource: String): @NlsSafe String { val serverPath = GitLabServerPath("http://base/url") - val projectPath = GitLabProjectPath("test-account", "mr-test") - val projectCoordinates = GitLabProjectCoordinates(serverPath, projectPath) - val projectId = GitLabRestIdData("testRestId") - val converter = GitLabMarkdownToHtmlConverter(project, gitRepository, projectCoordinates, projectId) + val projectId = "1" + val projectFullPath = extractProjectPath("test-account/mr-test") ?: error("Failed to extract project path") + val converter = GitLabMarkdownToHtmlConverter(project, gitRepository, serverPath, projectId, projectFullPath) return converter.convertToHtml(markdownSource) } } \ No newline at end of file diff --git a/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/util/GitLabProjectPathTest.kt b/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/util/GitLabProjectPathTest.kt new file mode 100644 index 000000000000..42377b68a24a --- /dev/null +++ b/plugins/gitlab/gitlab-core/test/org/jetbrains/plugins/gitlab/util/GitLabProjectPathTest.kt @@ -0,0 +1,86 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gitlab.util + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class GitLabProjectPathTest { + + @Test + fun `extractProjectPath with valid path`() { + val result = GitLabProjectPath.extractProjectPath("owner/project") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("owner") + assertThat(result?.name).isEqualTo("project") + } + + @Test + fun `extractProjectPath with nested owner path`() { + val result = GitLabProjectPath.extractProjectPath("group/subgroup/project") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("group/subgroup") + assertThat(result?.name).isEqualTo("project") + } + + @Test + fun `extractProjectPath with deeply nested path`() { + val result = GitLabProjectPath.extractProjectPath("org/team/subteam/project") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("org/team/subteam") + assertThat(result?.name).isEqualTo("project") + } + + @Test + fun `extractProjectPath with no slash returns null`() { + val result = GitLabProjectPath.extractProjectPath("project") + assertThat(result).isNull() + } + + @Test + fun `extractProjectPath with empty string returns null`() { + val result = GitLabProjectPath.extractProjectPath("") + assertThat(result).isNull() + } + + @Test + fun `extractProjectPath with only slash returns null`() { + val result = GitLabProjectPath.extractProjectPath("/") + assertThat(result).isNull() + } + + @Test + fun `extractProjectPath with trailing slash returns null`() { + val result = GitLabProjectPath.extractProjectPath("owner/project/") + assertThat(result).isNull() + } + + @Test + fun `extractProjectPath with leading slash`() { + val result = GitLabProjectPath.extractProjectPath("/owner/project") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("/owner") + assertThat(result?.name).isEqualTo("project") + } + + @Test + fun `extractProjectPath with empty owner returns null`() { + val result = GitLabProjectPath.extractProjectPath("/project") + assertThat(result).isNull() + } + + @Test + fun `extractProjectPath with whitespace in path`() { + val result = GitLabProjectPath.extractProjectPath("owner with spaces/project name") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("owner with spaces") + assertThat(result?.name).isEqualTo("project name") + } + + @Test + fun `extractProjectPath with special characters`() { + val result = GitLabProjectPath.extractProjectPath("owner-name/project.name") + assertThat(result).isNotNull + assertThat(result?.owner).isEqualTo("owner-name") + assertThat(result?.name).isEqualTo("project.name") + } +} diff --git a/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTest.kt b/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTest.kt index 2d2e58a9b656..78dcc2ea40a7 100644 --- a/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTest.kt +++ b/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTest.kt @@ -81,7 +81,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(9, 0))) requiresAuthentication { api -> - val commits = api.rest.loadMergeRequestCommits(api.getMergeRequestCommitsURI(glTest1Coordinates, "2")).body() + val commits = api.rest.loadMergeRequestCommits(api.rest.getMergeRequestCommitsURI(glTest1ProjectId, "2")).body() assertIterableEquals(glTest1Mr2CommitShortShas, commits.map { it.shortId }) } @@ -92,7 +92,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(inRange(v(9, 0), v(15, 7))) requiresAuthentication { api -> - val changes = api.rest.loadMergeRequestChanges(api.getMergeRequestChangesURI(glTest1Coordinates, "2")).body() + val changes = api.rest.loadMergeRequestChanges(api.rest.getMergeRequestChangesURI(glTest1ProjectId, "2")).body() assertIterableEquals(glTest1Mr2ChangedFiles, changes.changes.map { it.newPath }) } @@ -103,7 +103,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(15, 7))) requiresAuthentication { api -> - val diffs = api.rest.loadMergeRequestDiffs(api.getMergeRequestDiffsURI(glTest1Coordinates, "2", 1)).body() + val diffs = api.rest.loadMergeRequestDiffs(api.rest.getMergeRequestDiffsURI(glTest1ProjectId, "2", 1)).body() assertIterableEquals(glTest1Mr2ChangedFiles, diffs.map { it.newPath }) } @@ -114,7 +114,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(7, 0))) requiresAuthentication { api -> - val diffs = api.rest.loadCommitDiffs(getCommitDiffsURI(glTest1Coordinates, glTest1Mr2CommitShortShas[0])).body() + val diffs = api.rest.loadCommitDiffs(api.rest.getCommitDiffsURI(glTest1ProjectId, glTest1Mr2CommitShortShas[0])).body() assertIterableEquals(glTest1Mr2ChangedFiles, diffs.map { it.newPath }) } @@ -125,7 +125,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(7, 0))) requiresAuthentication { api -> - val commit = api.rest.loadCommit(glTest1Coordinates, glTest1Mr2CommitShortShas[0]).body() + val commit = api.rest.loadCommit(glTest1ProjectId, glTest1Mr2CommitShortShas[0]).body() assertEquals(glTest1Mr2CommitShas[0], commit.id) } @@ -136,7 +136,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(10, 6))) requiresAuthentication { api -> - val uri = getMergeRequestDiscussionsUri(glTest1Coordinates, "2") + val uri = api.rest.getMergeRequestDiscussionsUri(glTest1ProjectId, "2") val discussions = ApiPageUtil.createPagesFlowByLinkHeader(uri) { api.rest.loadUpdatableJsonList( GitLabApiRequestName.REST_GET_MERGE_REQUEST_DISCUSSIONS, it @@ -160,14 +160,14 @@ class GitLabApiTest : GitLabApiTestCase() { requiresAuthentication { api -> val randomId = Random.nextLong() val initialBody = "This is a new comment! ID=$randomId" - val addNoteResult = api.rest.addNote(volatileProjectCoordinates, volatileProjectMr1Iid, initialBody) + val addNoteResult = api.rest.addNote(volatileProjectId, volatileProjectMr1Iid, initialBody) assertNotNull(addNoteResult) val addNoteResultValue = addNoteResult.body() assertNotNull(addNoteResultValue) val nextBody = "Changed comment! ID=$randomId" - val updateNoteResult = api.rest.updateNote(volatileProjectCoordinates, + val updateNoteResult = api.rest.updateNote(volatileProjectId, volatileProjectMr1Iid, addNoteResultValue.id.toString(), addNoteResultValue.notes.first().id.toString(), @@ -176,19 +176,19 @@ class GitLabApiTest : GitLabApiTestCase() { // Check body changed // NOTE: WILL NOT WORK ON A CONSTANTLY RUNNING SERVER BECAUSE OF PAGINATION - val discussions = api.rest.loadMergeRequestDiscussions(volatileProjectCoordinates, volatileProjectMr1Iid).body() + val discussions = api.rest.loadMergeRequestDiscussions(volatileProjectId, volatileProjectMr1Iid).body() val updatedNote = discussions.find { it.id.toString() == addNoteResultValue.id.toString() } assertNotNull(updatedNote) assertEquals(nextBody, updatedNote.notes[0].body) - val deleteNoteResult = api.rest.deleteNote(volatileProjectCoordinates, + val deleteNoteResult = api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, updatedNote.id.toString(), updatedNote.notes.first().id.toString()).body() assertNotNull(deleteNoteResult) // Check is deleted - val deletedNote = api.rest.loadMergeRequestDiscussions(volatileProjectCoordinates, volatileProjectMr1Iid).body() + val deletedNote = api.rest.loadMergeRequestDiscussions(volatileProjectId, volatileProjectMr1Iid).body() .find { it.id.toString() == addNoteResultValue.id.toString() } assertNull(deletedNote) @@ -203,14 +203,14 @@ class GitLabApiTest : GitLabApiTestCase() { val randomId = Random.nextLong() val initialBody = "This is a diff note! ID=$randomId" val addNoteResult = api.rest.addDiffNote( - volatileProjectCoordinates, + volatileProjectId, volatileProjectMr1Iid, GitLabDiffPositionInput("bd857928", "bd857928", 1, "063282e5", 1, DiffPathsInputDTO("README.md", null)), initialBody ).body() assertNotNull(addNoteResult) - val deleteNoteResult = api.rest.deleteNote(volatileProjectCoordinates, + val deleteNoteResult = api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, addNoteResult.id.toString(), addNoteResult.notes[0].id.toString()).body() @@ -226,7 +226,7 @@ class GitLabApiTest : GitLabApiTestCase() { val randomId = Random.nextLong() val initialBody = "This is a note! ID=$randomId" val addNoteResult = api.rest.addNote( - volatileProjectCoordinates, + volatileProjectId, volatileProjectMr1Iid, initialBody ).body() @@ -234,7 +234,7 @@ class GitLabApiTest : GitLabApiTestCase() { val replyBody = "This is a reply! ID=$randomId" val addNoteResult2 = api.rest.createReplyNote( - volatileProjectCoordinates, + volatileProjectId, volatileProjectMr1Iid, addNoteResult.id.toString(), replyBody @@ -242,18 +242,18 @@ class GitLabApiTest : GitLabApiTestCase() { assertNotNull(addNoteResult2) // NOTE: WILL NOT WORK ON A CONSTANTLY RUNNING SERVER BECAUSE OF PAGINATION - val result = api.rest.loadMergeRequestDiscussions(volatileProjectCoordinates, volatileProjectMr1Iid).body() + val result = api.rest.loadMergeRequestDiscussions(volatileProjectId, volatileProjectMr1Iid).body() assertNotNull(result) val discussion = result.find { addNoteResult.notes[0].body.contains(randomId.toString()) } assertNotNull(discussion) val deleteNoteResult1 = - api.rest.deleteNote(volatileProjectCoordinates, volatileProjectMr1Iid, discussion.id.toString(), discussion.notes[0].id.toString()) + api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, discussion.id.toString(), discussion.notes[0].id.toString()) .body() assertNotNull(deleteNoteResult1) val deleteNoteResult2 = - api.rest.deleteNote(volatileProjectCoordinates, volatileProjectMr1Iid, discussion.id.toString(), discussion.notes[1].id.toString()) + api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, discussion.id.toString(), discussion.notes[1].id.toString()) .body() assertNotNull(deleteNoteResult2) } @@ -267,7 +267,7 @@ class GitLabApiTest : GitLabApiTestCase() { val randomId = Random.nextLong() val initialBody = "This is a diff note! ID=$randomId" val addNoteResult = api.rest.addDiffNote( - volatileProjectCoordinates, + volatileProjectId, volatileProjectMr1Iid, GitLabDiffPositionInput("bd857928", "bd857928", 1, "063282e5", 1, DiffPathsInputDTO("README.md", null)), @@ -277,7 +277,7 @@ class GitLabApiTest : GitLabApiTestCase() { val replyBody = "This is a reply! ID=$randomId" val addNoteResult2 = api.rest.createReplyNote( - volatileProjectCoordinates, + volatileProjectId, volatileProjectMr1Iid, addNoteResult.id.toString(), replyBody @@ -285,7 +285,7 @@ class GitLabApiTest : GitLabApiTestCase() { assertNotNull(addNoteResult2) // NOTE: WILL NOT WORK ON A CONSTANTLY RUNNING SERVER BECAUSE OF PAGINATION - val result1 = api.rest.loadMergeRequestDiscussions(volatileProjectCoordinates, volatileProjectMr1Iid).body() + val result1 = api.rest.loadMergeRequestDiscussions(volatileProjectId, volatileProjectMr1Iid).body() assertNotNull(result1) val discussion1 = result1.find { addNoteResult.notes[0].body.contains(randomId.toString()) } @@ -293,24 +293,24 @@ class GitLabApiTest : GitLabApiTestCase() { assertFalse(discussion1.notes[0].resolved) val resolveNoteResult = - api.rest.changeMergeRequestDiscussionResolve(volatileProjectCoordinates, volatileProjectMr1Iid, discussion1.id.toString(), true) + api.rest.changeMergeRequestDiscussionResolve(volatileProjectId, volatileProjectMr1Iid, discussion1.id.toString(), true) .body() assertNotNull(resolveNoteResult) // Confirm is now resolved - val result2 = api.rest.loadMergeRequestDiscussions(volatileProjectCoordinates, volatileProjectMr1Iid).body() + val result2 = api.rest.loadMergeRequestDiscussions(volatileProjectId, volatileProjectMr1Iid).body() assertNotNull(result2) val discussion2 = result2.find { addNoteResult.notes[0].body.contains(randomId.toString()) } assertNotNull(discussion2) assertTrue(discussion2.notes[0].resolved) - val deleteNoteResult1 = api.rest.deleteNote(volatileProjectCoordinates, + val deleteNoteResult1 = api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, discussion2.id.toString(), discussion2.notes[0].id.toString()).body() assertNotNull(deleteNoteResult1) - val deleteNoteResult2 = api.rest.deleteNote(volatileProjectCoordinates, + val deleteNoteResult2 = api.rest.deleteNote(volatileProjectId, volatileProjectMr1Iid, discussion2.id.toString(), discussion2.notes[1].id.toString()).body() @@ -323,7 +323,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(15, 9))) requiresAuthentication { api -> - val uri = getMergeRequestDraftNotesUri(glTest1Coordinates, glTest1Mr2Iid) + val uri = api.rest.getMergeRequestDraftNotesUri(glTest1ProjectId, glTest1Mr2Iid) val draftNotes = ApiPageUtil.createPagesFlowByLinkHeader(uri) { api.rest.loadUpdatableJsonList( GitLabApiRequestName.REST_GET_DRAFT_NOTES, it @@ -343,7 +343,7 @@ class GitLabApiTest : GitLabApiTestCase() { requiresAuthentication { api -> val mrs = api.rest.loadUpdatableJsonList( GitLabApiRequestName.REST_GET_MERGE_REQUESTS, - getMergeRequestListURI(glTest1Coordinates, "search=important") + api.rest.getMergeRequestListURI(glTest1ProjectId, "search=important") ).body() assertNotNull(mrs) @@ -356,7 +356,8 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(12, 0))) requiresAuthentication { api -> - val mr = api.graphQL.loadMergeRequest(glTest1Coordinates, "2").body() + val testProject1 = glTest1Coordinates + val mr = api.graphQL.loadMergeRequest(testProject1.projectPath, "2").body() assertNotNull(mr) assertEquals("2", mr.iid) @@ -368,7 +369,8 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(13, 1))) requiresAuthentication { api -> - val mrs = api.graphQL.findMergeRequestsByBranch(glTest1Coordinates, GitLabMergeRequestState.ALL, "changes-on-b").body() + val testProject1 = glTest1Coordinates + val mrs = api.graphQL.findMergeRequestsByBranch(testProject1.projectPath, GitLabMergeRequestState.ALL, "changes-on-b").body() assertNotNull(mrs) assertEquals(listOf("3"), mrs.nodes.map { it.iid }) @@ -382,7 +384,7 @@ class GitLabApiTest : GitLabApiTestCase() { requiresAuthentication { api -> val reloadRequest = MutableSharedFlow(1).withInitial(Unit) val loader = startGitLabRestETagListLoaderIn(backgroundScope, - getMergeRequestStateEventsUri(glTest1Coordinates, "1"), + api.rest.getMergeRequestStateEventsUri(glTest1ProjectId, "1"), { it.id }, reloadRequest, shouldTryToLoadAll = false) { uri, eTag -> @@ -404,7 +406,7 @@ class GitLabApiTest : GitLabApiTestCase() { requiresAuthentication { api -> val reloadRequest = MutableSharedFlow(1).withInitial(Unit) val loader = startGitLabRestETagListLoaderIn(backgroundScope, - getMergeRequestLabelEventsUri(glTest1Coordinates, "1"), + api.rest.getMergeRequestLabelEventsUri(glTest1ProjectId, "1"), { it.id }, reloadRequest, shouldTryToLoadAll = false) { uri, eTag -> @@ -426,7 +428,7 @@ class GitLabApiTest : GitLabApiTestCase() { requiresAuthentication { api -> val reloadRequest = MutableSharedFlow(1).withInitial(Unit) val loader = startGitLabRestETagListLoaderIn(backgroundScope, - getMergeRequestMilestoneEventsUri(glTest1Coordinates, "1"), + api.rest.getMergeRequestMilestoneEventsUri(glTest1ProjectId, "1"), { it.id }, reloadRequest, shouldTryToLoadAll = false) { uri, eTag -> @@ -449,8 +451,8 @@ class GitLabApiTest : GitLabApiTestCase() { } requiresAuthentication { api -> - api.rest.mergeRequestApprove(volatileProjectCoordinates, volatileProjectMr2Iid).body() - api.rest.mergeRequestUnApprove(volatileProjectCoordinates, volatileProjectMr2Iid).body() + api.rest.mergeRequestApprove(volatileProjectId, volatileProjectMr2Iid).body() + api.rest.mergeRequestUnApprove(volatileProjectId, volatileProjectMr2Iid).body() } } @@ -459,7 +461,8 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(13, 1))) requiresAuthentication { api -> - val flow = api.graphQL.createAllProjectLabelsFlow(glTests2Coordinates) + val glTests2Project = glTests2Coordinates.projectPath + val flow = api.graphQL.createAllProjectLabelsFlow(glTests2Project) val labels = flow.foldToList().sortedBy { it.title } assertIterableEquals(listOf(testsGroupLabel1, testsGroupLabel2, glTests2Label1, glTests2Label2), labels) @@ -471,7 +474,7 @@ class GitLabApiTest : GitLabApiTestCase() { checkVersion(after(v(9, 0))) requiresAuthentication { api -> - val users = api.rest.getProjectUsers(getProjectUsersURI(glTests2Coordinates)).body() + val users = api.rest.getProjectUsers(api.rest.getProjectUsersURI(glTests2ProjectId)).body() assertTrue(users.map { it.username }.contains(rootUsername)) } @@ -560,7 +563,8 @@ class GitLabApiTest : GitLabApiTestCase() { assertNotNull(it, "test-png.png resource should exist") val name = "test-image" val filename = "$name.png" - val uploadResult = api.rest.markdownUploadFile(glTest1Coordinates, filename, "image/png", it).body() + + val uploadResult = api.rest.markdownUploadFile(glTest1ProjectId, filename, "image/png", it).body() assertNotNull(uploadResult) val markdown = uploadResult.markdown assertTrue(markdown.startsWith("![$name](/uploads/"), diff --git a/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTestCase.kt b/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTestCase.kt index 2de34266d225..95fd30744569 100644 --- a/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTestCase.kt +++ b/plugins/gitlab/gitlab-core/testApi/org/jetbrains/plugins/gitlab/apitests/GitLabApiTestCase.kt @@ -68,20 +68,20 @@ abstract class GitLabApiTestCase { val glTests2Coordinates = GitLabProjectCoordinates(server, glTests2Project) val glTests2Label1 = GitLabLabelGQLDTO("label1", "#FFFFFF") val glTests2Label2 = GitLabLabelGQLDTO("label2", "#FFFFFF") + val glTests2ProjectId = "6" val glTest1Project = GitLabProjectPath("root", "gl-test-1") val glTest1Coordinates = GitLabProjectCoordinates(server, glTest1Project) + val glTest1ProjectId = "2" val glTest1Mr2Iid = "2" val glTest1Mr2CommitShas = listOf("6a7d70b5df2d77ad792bca08c6fb14e29ae4ad04") val glTest1Mr2CommitShortShas = listOf("6a7d70b5") val glTest1Mr2ChangedFiles = listOf("a/important.txt") - val volatileProject = GitLabProjectPath("volatile", "volatile-project") - val volatileProjectCoordinates = GitLabProjectCoordinates(server, volatileProject) val volatileProjectMr1Iid = "1" - val volatileProjectMr1Gid = "gid://gitlab/MergeRequest/5" val volatileProjectMr2Iid = "2" + val volatileProjectId = "4" } companion object { diff --git a/plugins/gitlab/gitlab-git/src/com/intellij/vcs/gitlab/git/ui/GitLabShareProjectDialogViewModel.kt b/plugins/gitlab/gitlab-git/src/com/intellij/vcs/gitlab/git/ui/GitLabShareProjectDialogViewModel.kt index 2990364413fc..7fdcb5ea0b08 100644 --- a/plugins/gitlab/gitlab-git/src/com/intellij/vcs/gitlab/git/ui/GitLabShareProjectDialogViewModel.kt +++ b/plugins/gitlab/gitlab-git/src/com/intellij/vcs/gitlab/git/ui/GitLabShareProjectDialogViewModel.kt @@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.transformLatest import org.jetbrains.plugins.gitlab.GitLabServersManager import org.jetbrains.plugins.gitlab.api.GitLabApi import org.jetbrains.plugins.gitlab.api.GitLabApiManager -import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates import org.jetbrains.plugins.gitlab.api.dto.WithGitLabNamespace import org.jetbrains.plugins.gitlab.api.request.findProject import org.jetbrains.plugins.gitlab.api.request.getMemberNamespacesForShare @@ -164,7 +163,7 @@ internal class GitLabShareProjectDialogViewModel( if (account == null || api == null || namespace == null) return@combine false LOG.info("Checking for existing repositories at coordinates: ${account.server}/${namespace.fullPath}/$name") - api.graphQL.findProject(GitLabProjectCoordinates(account.server, GitLabProjectPath(namespace.fullPath, name))).body() != null + api.graphQL.findProject(GitLabProjectPath(namespace.fullPath, name)).body() != null }.stateIn(cs, SharingStarted.Eagerly, false) private val accountValidationError: StateFlow<@NlsContexts.DialogMessage String?> =