mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
[gitlab] Try to check permissions for multiple reviewers through work item widget
* #IDEA-327475 GitOrigin-RevId: bc39b2e7d8edc666cbc64c7f280d95165f53ae90
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a4b9906d0e
commit
bae92e77ae
@@ -0,0 +1,9 @@
|
||||
fragment workItem on WorkItem {
|
||||
workItemType {
|
||||
name
|
||||
}
|
||||
widgets {
|
||||
__typename
|
||||
...workItemWidgetAssignees
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fragment workItemWidgetAssignees on WorkItemWidgetAssignees {
|
||||
allowsMultipleAssignees
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
query($fullPath: ID!, $pageSize: Int = 100, $cursor: String) {
|
||||
project(fullPath: $fullPath) {
|
||||
workItems(first: $pageSize, after: $cursor) {
|
||||
nodes {
|
||||
...workItem
|
||||
}
|
||||
pageInfo {
|
||||
...pageInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ enum class GitLabGQLQuery(val filePath: String) {
|
||||
GET_PROJECT_LABELS("graphql/query/getProjectLabels.graphql"),
|
||||
@SinceGitLab("12.0")
|
||||
GET_PROJECT_REPOSITORY("graphql/query/getProjectRepository.graphql"),
|
||||
@SinceGitLab("15.2")
|
||||
GET_PROJECT_WORK_ITEMS("graphql/query/getProjectWidgets.graphql"),
|
||||
@SinceGitLab("13.0")
|
||||
GET_MEMBER_PROJECTS("graphql/query/getMemberProjects.graphql"),
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2000-2023 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
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||
import com.intellij.collaboration.api.dto.GraphQLFragment
|
||||
import org.jetbrains.plugins.gitlab.api.SinceGitLab
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO.GitLabWidgetDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO.GitLabWidgetDTO.*
|
||||
|
||||
@SinceGitLab("14.8")
|
||||
@GraphQLFragment("graphql/fragment/workItem.graphql")
|
||||
data class GitLabWorkItemDTO(
|
||||
val workItemType: WorkItemType,
|
||||
val widgets: List<GitLabWidgetDTO>
|
||||
) {
|
||||
data class WorkItemType(val name: String) {
|
||||
companion object {
|
||||
const val ISSUE_TYPE = "Issue"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
include = JsonTypeInfo.As.PROPERTY,
|
||||
property = "__typename",
|
||||
visible = false,
|
||||
defaultImpl = GitLabWidgetDTO::class
|
||||
)
|
||||
@JsonSubTypes(
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetAssignees", value = WorkItemWidgetAssignees::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetAwardEmoji", value = WorkItemWidgetAwardEmoji::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetCurrentUserTodos", value = WorkItemWidgetCurrentUserTodos::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetDescription", value = WorkItemWidgetDescription::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetHierarchy", value = WorkItemWidgetHierarchy::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetLabels", value = WorkItemWidgetLabels::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetLinkedItems", value = WorkItemWidgetLinkedItems::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetMilestone", value = WorkItemWidgetMilestone::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetNotes", value = WorkItemWidgetNotes::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetNotifications", value = WorkItemWidgetNotifications::class),
|
||||
JsonSubTypes.Type(name = "WorkItemWidgetStartAndDueDate", value = WorkItemWidgetStartAndDueDate::class),
|
||||
)
|
||||
@SinceGitLab("15.1")
|
||||
interface GitLabWidgetDTO {
|
||||
@SinceGitLab("15.2")
|
||||
@GraphQLFragment("graphql/fragment/workItemWidgetAssignees.graphql")
|
||||
data class WorkItemWidgetAssignees(val allowsMultipleAssignees: Boolean) : GitLabWidgetDTO
|
||||
class WorkItemWidgetAwardEmoji : GitLabWidgetDTO
|
||||
class WorkItemWidgetCurrentUserTodos : GitLabWidgetDTO
|
||||
class WorkItemWidgetDescription : GitLabWidgetDTO
|
||||
class WorkItemWidgetHierarchy : GitLabWidgetDTO
|
||||
class WorkItemWidgetLabels : GitLabWidgetDTO
|
||||
class WorkItemWidgetLinkedItems : GitLabWidgetDTO
|
||||
class WorkItemWidgetMilestone : GitLabWidgetDTO
|
||||
class WorkItemWidgetNotes : GitLabWidgetDTO
|
||||
class WorkItemWidgetNotifications : GitLabWidgetDTO
|
||||
class WorkItemWidgetStartAndDueDate : GitLabWidgetDTO
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.plugins.gitlab.api.dto.GitLabLabelDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabNamespaceRestDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabRepositoryDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserRestDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestDTO
|
||||
import org.jetbrains.plugins.gitlab.util.GitLabApiRequestName
|
||||
import java.net.URI
|
||||
@@ -34,6 +35,18 @@ fun GitLabApi.GraphQL.createAllProjectLabelsFlow(project: GitLabProjectCoordinat
|
||||
}
|
||||
}.map { it.nodes }
|
||||
|
||||
@SinceGitLab("15.2")
|
||||
fun GitLabApi.GraphQL.createAllWorkItemsFlow(project: GitLabProjectCoordinates): Flow<List<GitLabWorkItemDTO>> =
|
||||
ApiPageUtil.createGQLPagesFlow { page ->
|
||||
val parameters = page.asParameters() + mapOf(
|
||||
"fullPath" to project.projectPath.fullPath()
|
||||
)
|
||||
val request = gitLabQuery(GitLabGQLQuery.GET_PROJECT_WORK_ITEMS, parameters)
|
||||
withErrorStats(GitLabGQLQuery.GET_PROJECT_WORK_ITEMS) {
|
||||
loadResponse<WorkItemConnection>(request, "project", "workItems").body()
|
||||
}
|
||||
}.map { it.nodes }
|
||||
|
||||
@SinceGitLab("7.0", note = "No exact version")
|
||||
fun getProjectUsersURI(project: GitLabProjectCoordinates) = project.restApiUri.resolveRelative("users")
|
||||
|
||||
@@ -91,6 +104,9 @@ suspend fun GitLabApi.GraphQL.createMergeRequest(
|
||||
private class LabelConnection(pageInfo: GraphQLCursorPageInfoDTO, nodes: List<GitLabLabelDTO>)
|
||||
: GraphQLConnectionDTO<GitLabLabelDTO>(pageInfo, nodes)
|
||||
|
||||
private class WorkItemConnection(pageInfo: GraphQLCursorPageInfoDTO, nodes: List<GitLabWorkItemDTO>)
|
||||
: GraphQLConnectionDTO<GitLabWorkItemDTO>(pageInfo, nodes)
|
||||
|
||||
private class GitLabCreateMergeRequestResult(
|
||||
mergeRequest: GitLabMergeRequestDTO,
|
||||
errors: List<String>?,
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.jetbrains.plugins.gitlab.api.*
|
||||
import org.jetbrains.plugins.gitlab.api.data.GitLabPlan
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabLabelDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO.GitLabWidgetDTO.WorkItemWidgetAssignees
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabWorkItemDTO.WorkItemType
|
||||
import org.jetbrains.plugins.gitlab.api.request.*
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.dto.GitLabMergeRequestDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequest
|
||||
@@ -31,7 +33,7 @@ interface GitLabProject {
|
||||
val labels: SharedFlow<Result<List<GitLabLabelDTO>>>
|
||||
val members: SharedFlow<Result<List<GitLabUserDTO>>>
|
||||
val defaultBranch: Deferred<String>
|
||||
val plan: Deferred<GitLabPlan?>
|
||||
val allowsMultipleReviewers: SharedFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Creates a merge request on the GitLab server and returns a DTO containing the merge request
|
||||
@@ -81,13 +83,30 @@ class GitLabLazyProject(
|
||||
projectRepository.rootRef
|
||||
}
|
||||
|
||||
override val plan: Deferred<GitLabPlan?> = cs.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
|
||||
private val plan: Deferred<GitLabPlan?> = cs.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
|
||||
runCatchingUser {
|
||||
val namespace = api.rest.getProjectNamespace(projectMapping.repository.projectPath.owner).body()
|
||||
namespace?.plan
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
// TODO: Change the implementation after adding `allowsMultipleReviewers` field to the API
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/issues/431829
|
||||
override val allowsMultipleReviewers: SharedFlow<Boolean> = channelFlow {
|
||||
val glPlan = plan.await()
|
||||
if (glPlan != null) {
|
||||
send(glPlan != GitLabPlan.FREE)
|
||||
return@channelFlow
|
||||
}
|
||||
|
||||
if (glMetadata != null && glMetadata.version >= GitLabVersion(15, 2)) {
|
||||
send(getAllowsMultipleAssigneesPropertyFromIssueWidget())
|
||||
return@channelFlow
|
||||
}
|
||||
|
||||
send(false)
|
||||
}.modelFlow(parentCs, LOG)
|
||||
|
||||
@Throws(GitLabGraphQLMutationException::class)
|
||||
override suspend fun createMergeRequestAndAwaitCompletion(sourceBranch: String, targetBranch: String, title: String): GitLabMergeRequestDTO {
|
||||
return withContext(cs.coroutineContext + Dispatchers.IO) {
|
||||
@@ -127,6 +146,29 @@ class GitLabLazyProject(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getAllowsMultipleAssigneesPropertyFromIssueWidget(): Boolean {
|
||||
val widgetAssignees: WorkItemWidgetAssignees? = resultListFlow {
|
||||
api.graphQL.createAllWorkItemsFlow(projectMapping.repository)
|
||||
}.transformWhile { resultedWorkItems ->
|
||||
val items = resultedWorkItems.getOrNull() ?: return@transformWhile false
|
||||
val widget = items.find { workItem -> workItem.workItemType.name == WorkItemType.ISSUE_TYPE }
|
||||
?.widgets
|
||||
?.asSequence()
|
||||
?.filterIsInstance<WorkItemWidgetAssignees>()
|
||||
?.first()
|
||||
|
||||
if (widget != null) {
|
||||
emit(widget)
|
||||
return@transformWhile false
|
||||
}
|
||||
else {
|
||||
return@transformWhile true
|
||||
}
|
||||
}.firstOrNull()
|
||||
|
||||
return widgetAssignees?.allowsMultipleAssignees ?: false
|
||||
}
|
||||
|
||||
private fun <T> resultListFlow(flowProvider: () -> Flow<List<T>>): Flow<Result<List<T>>> = channelFlow<Result<List<T>>> {
|
||||
runCatchingUser {
|
||||
val loadedItems = mutableListOf<T>()
|
||||
|
||||
@@ -25,7 +25,6 @@ import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.future.await
|
||||
import org.jetbrains.plugins.gitlab.GitLabProjectsManager
|
||||
import org.jetbrains.plugins.gitlab.api.data.GitLabPlan
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabProject
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.util.GitLabMergeRequestReviewersUtil
|
||||
@@ -40,7 +39,7 @@ internal interface GitLabMergeRequestCreateViewModel {
|
||||
|
||||
val isBusy: Flow<Boolean>
|
||||
|
||||
val plan: Deferred<GitLabPlan?>
|
||||
val allowsMultipleReviewers: Flow<Boolean>
|
||||
val branchState: Flow<BranchState?>
|
||||
|
||||
val existingMergeRequest: Flow<String?>
|
||||
@@ -78,7 +77,7 @@ internal class GitLabMergeRequestCreateViewModelImpl(
|
||||
|
||||
override val isBusy: Flow<Boolean> = taskLauncher.busy
|
||||
|
||||
override val plan: Deferred<GitLabPlan?> = projectData.plan
|
||||
override val allowsMultipleReviewers: Flow<Boolean> = projectData.allowsMultipleReviewers
|
||||
|
||||
private val listenableProgressIndicator = ListenableProgressIndicator()
|
||||
override val creatingProgressText: Flow<String?> = callbackFlow {
|
||||
@@ -168,11 +167,12 @@ internal class GitLabMergeRequestCreateViewModelImpl(
|
||||
|
||||
override fun adjustReviewer(point: RelativePoint) {
|
||||
cs.launchNow(Dispatchers.Main) {
|
||||
val allowsMultipleReviewers = allowsMultipleReviewers.first()
|
||||
val originalReviewersIds = adjustedReviewers.value.mapTo(mutableSetOf<String>(), GitLabUserDTO::id)
|
||||
val updatedReviewers = if (plan.await() == GitLabPlan.FREE)
|
||||
GitLabMergeRequestReviewersUtil.selectReviewer(point, originalReviewersIds, potentialReviewers, avatarIconProvider)
|
||||
else
|
||||
val updatedReviewers = if (allowsMultipleReviewers == true)
|
||||
GitLabMergeRequestReviewersUtil.selectReviewers(point, originalReviewersIds, potentialReviewers, avatarIconProvider)
|
||||
else
|
||||
GitLabMergeRequestReviewersUtil.selectReviewer(point, originalReviewersIds, potentialReviewers, avatarIconProvider)
|
||||
|
||||
updatedReviewers ?: return@launchNow
|
||||
_adjustedReviewers.value = updatedReviewers
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.ui.awt.RelativePoint
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.plugins.gitlab.api.SinceGitLab
|
||||
import org.jetbrains.plugins.gitlab.api.data.GitLabPlan
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabCiJobDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabReviewerDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
@@ -39,7 +38,7 @@ import org.jetbrains.plugins.gitlab.util.GitLabBundle
|
||||
internal interface GitLabMergeRequestReviewFlowViewModel : CodeReviewFlowViewModel<GitLabReviewerDTO> {
|
||||
val isBusy: Flow<Boolean>
|
||||
|
||||
val plan: Deferred<GitLabPlan?>
|
||||
val allowsMultipleReviewers: Flow<Boolean>
|
||||
|
||||
val currentUser: GitLabUserDTO
|
||||
val author: GitLabUserDTO
|
||||
@@ -105,7 +104,7 @@ internal class GitLabMergeRequestReviewFlowViewModelImpl(
|
||||
|
||||
override val isBusy: Flow<Boolean> = taskLauncher.busy
|
||||
|
||||
override val plan: Deferred<GitLabPlan?> = projectData.plan
|
||||
override val allowsMultipleReviewers: Flow<Boolean> = projectData.allowsMultipleReviewers
|
||||
|
||||
override val author: GitLabUserDTO = mergeRequest.author
|
||||
|
||||
@@ -251,11 +250,12 @@ internal class GitLabMergeRequestReviewFlowViewModelImpl(
|
||||
|
||||
override fun adjustReviewers(point: RelativePoint) {
|
||||
scope.launchNow(Dispatchers.Main) {
|
||||
val allowsMultipleReviewers = allowsMultipleReviewers.first()
|
||||
val originalReviewersIds = reviewers.value.mapTo(mutableSetOf<String>(), GitLabUserDTO::id)
|
||||
val updatedReviewers = if (plan.await() == GitLabPlan.FREE)
|
||||
GitLabMergeRequestReviewersUtil.selectReviewer(point, originalReviewersIds, potentialReviewers, avatarIconsProvider)
|
||||
else
|
||||
val updatedReviewers = if (allowsMultipleReviewers == true)
|
||||
GitLabMergeRequestReviewersUtil.selectReviewers(point, originalReviewersIds, potentialReviewers, avatarIconsProvider)
|
||||
else
|
||||
GitLabMergeRequestReviewersUtil.selectReviewer(point, originalReviewersIds, potentialReviewers, avatarIconsProvider)
|
||||
|
||||
updatedReviewers ?: return@launchNow
|
||||
setReviewers(updatedReviewers)
|
||||
@@ -264,11 +264,12 @@ internal class GitLabMergeRequestReviewFlowViewModelImpl(
|
||||
|
||||
@SinceGitLab("13.8")
|
||||
override fun setMyselfAsReviewer() = runAction {
|
||||
if (plan.await() == GitLabPlan.FREE) {
|
||||
mergeRequest.setReviewers(listOf(currentUser))
|
||||
val allowsMultipleReviewers = allowsMultipleReviewers.first()
|
||||
if (allowsMultipleReviewers == true) {
|
||||
mergeRequest.setReviewers(listOf(currentUser) + reviewers.value)
|
||||
}
|
||||
else {
|
||||
mergeRequest.setReviewers(listOf(currentUser) + reviewers.value)
|
||||
mergeRequest.setReviewers(listOf(currentUser))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ internal object GitLabStatistics {
|
||||
//endregion
|
||||
|
||||
//region Counters
|
||||
private val COUNTERS_GROUP = EventLogGroup("vcs.gitlab.counters", version = 16)
|
||||
private val COUNTERS_GROUP = EventLogGroup("vcs.gitlab.counters", version = 17)
|
||||
|
||||
/**
|
||||
* Server metadata was fetched
|
||||
@@ -289,6 +289,7 @@ enum class GitLabApiRequestName {
|
||||
GQL_GET_MERGE_REQUEST_DISCUSSIONS,
|
||||
GQL_GET_PROJECT_LABELS,
|
||||
GQL_GET_PROJECT_REPOSITORY,
|
||||
GQL_GET_PROJECT_WORK_ITEMS,
|
||||
GQL_GET_MEMBER_PROJECTS,
|
||||
GQL_TOGGLE_MERGE_REQUEST_DISCUSSION_RESOLVE,
|
||||
GQL_CREATE_NOTE,
|
||||
@@ -314,6 +315,7 @@ enum class GitLabApiRequestName {
|
||||
GitLabGQLQuery.GET_MERGE_REQUEST_DISCUSSIONS -> GQL_GET_MERGE_REQUEST_DISCUSSIONS
|
||||
GitLabGQLQuery.GET_PROJECT_LABELS -> GQL_GET_PROJECT_LABELS
|
||||
GitLabGQLQuery.GET_PROJECT_REPOSITORY -> GQL_GET_PROJECT_REPOSITORY
|
||||
GitLabGQLQuery.GET_PROJECT_WORK_ITEMS -> GQL_GET_PROJECT_WORK_ITEMS
|
||||
GitLabGQLQuery.GET_MEMBER_PROJECTS -> GQL_GET_MEMBER_PROJECTS
|
||||
GitLabGQLQuery.TOGGLE_MERGE_REQUEST_DISCUSSION_RESOLVE -> GQL_TOGGLE_MERGE_REQUEST_DISCUSSION_RESOLVE
|
||||
GitLabGQLQuery.CREATE_NOTE -> GQL_CREATE_NOTE
|
||||
|
||||
Reference in New Issue
Block a user