mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 21:11:28 +07:00
[gitlab] extract MR discussions model
GitOrigin-RevId: 0e17fc8e31073e5c986fe5e1ea28225b0d1ff91d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9f3269a66a
commit
f3d215e735
@@ -24,4 +24,7 @@ class GraphQLRequestPagination private constructor(
|
||||
}
|
||||
|
||||
fun GraphQLRequestPagination.asParameters(): Map<String, Any?> =
|
||||
mapOf("pageSize" to pageSize, "cursor" to afterCursor)
|
||||
mapOf("pageSize" to pageSize, "cursor" to afterCursor)
|
||||
|
||||
fun GraphQLRequestPagination?.orDefault(): GraphQLRequestPagination =
|
||||
this ?: GraphQLRequestPagination.DEFAULT
|
||||
@@ -1,11 +1,11 @@
|
||||
// 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.mergerequest.api.request
|
||||
|
||||
import com.intellij.collaboration.api.data.GraphQLRequestPagination
|
||||
import com.intellij.collaboration.api.data.asParameters
|
||||
import com.intellij.collaboration.api.data.orDefault
|
||||
import com.intellij.collaboration.api.dto.GraphQLConnectionDTO
|
||||
import com.intellij.collaboration.api.dto.GraphQLCursorPageInfoDTO
|
||||
import com.intellij.collaboration.api.page.ApiPageUtil
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabApi
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabGQLQueries
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates
|
||||
@@ -13,16 +13,17 @@ import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestId
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
suspend fun GitLabApi.loadAllMergeRequestDiscussions(project: GitLabProjectCoordinates,
|
||||
mr: GitLabMergeRequestId): Flow<List<GitLabDiscussionDTO>> =
|
||||
ApiPageUtil.createGQLPagesFlow {
|
||||
val parameters = it.asParameters() + mapOf(
|
||||
"projectId" to project.projectPath.fullPath(),
|
||||
"mriid" to mr.iid
|
||||
)
|
||||
val request = gqlQuery(project.serverPath.gqlApiUri, GitLabGQLQueries.getMergeRequestDiscussions, parameters)
|
||||
loadGQLResponse(request, DiscussionConnection::class.java, "project", "mergeRequest", "discussions").body()
|
||||
}
|
||||
suspend fun GitLabApi.loadMergeRequestDiscussions(project: GitLabProjectCoordinates,
|
||||
mr: GitLabMergeRequestId,
|
||||
pagination: GraphQLRequestPagination? = null)
|
||||
: GraphQLConnectionDTO<GitLabDiscussionDTO>? {
|
||||
val parameters = pagination.orDefault().asParameters() + mapOf(
|
||||
"projectId" to project.projectPath.fullPath(),
|
||||
"mriid" to mr.iid
|
||||
)
|
||||
val request = gqlQuery(project.serverPath.gqlApiUri, GitLabGQLQueries.getMergeRequestDiscussions, parameters)
|
||||
return loadGQLResponse(request, DiscussionConnection::class.java, "project", "mergeRequest", "discussions").body()
|
||||
}
|
||||
|
||||
private class DiscussionConnection(pageInfo: GraphQLCursorPageInfoDTO, nodes: List<GitLabDiscussionDTO>)
|
||||
: GraphQLConnectionDTO<GitLabDiscussionDTO>(pageInfo, nodes)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.mergerequest.data
|
||||
|
||||
import com.intellij.collaboration.api.page.ApiPageUtil
|
||||
import com.intellij.collaboration.api.page.foldToList
|
||||
import com.intellij.collaboration.async.mapScoped
|
||||
import com.intellij.util.childScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestDiscussions
|
||||
|
||||
interface GitLabMergeRequestDiscussionsModel {
|
||||
val userDiscussions: Flow<List<GitLabDiscussion>>
|
||||
val systemDiscussions: Flow<List<GitLabDiscussionDTO>>
|
||||
}
|
||||
|
||||
class GitLabMergeRequestDiscussionsModelImpl(
|
||||
parentCs: CoroutineScope,
|
||||
private val connection: GitLabProjectConnection,
|
||||
private val mr: GitLabMergeRequestId
|
||||
) : GitLabMergeRequestDiscussionsModel {
|
||||
|
||||
private val cs = parentCs.childScope(Dispatchers.Default)
|
||||
|
||||
private val nonEmptyDiscussionsData = flow {
|
||||
ApiPageUtil.createGQLPagesFlow {
|
||||
connection.apiClient.loadMergeRequestDiscussions(connection.repo.repository, mr, it)
|
||||
}.map { discussions ->
|
||||
discussions.filter { it.notes.isNotEmpty() }
|
||||
}.foldToList()
|
||||
.let { emit(it) }
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
override val userDiscussions: Flow<List<GitLabDiscussion>> =
|
||||
nonEmptyDiscussionsData.mapScoped { discussions ->
|
||||
val cs = this
|
||||
discussions.filter { !it.notes.first().system }
|
||||
.map { LoadedGitLabDiscussion(cs, connection, it) }
|
||||
}
|
||||
|
||||
override val systemDiscussions: Flow<List<GitLabDiscussionDTO>> =
|
||||
nonEmptyDiscussionsData.map { discussions ->
|
||||
discussions.filter { it.notes.first().system }
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
// 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.mergerequest.ui.timeline
|
||||
|
||||
import com.intellij.collaboration.async.mapScoped
|
||||
import com.intellij.util.childScope
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadAllMergeRequestDiscussions
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestLabelEvents
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestMilestoneEvents
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestStateEvents
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestDiscussionsModelImpl
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestId
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.LoadedGitLabDiscussion
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.ui.timeline.GitLabMergeRequestTimelineViewModel.LoadingState
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
import java.util.*
|
||||
|
||||
interface GitLabMergeRequestTimelineViewModel {
|
||||
val timelineLoadingFlow: Flow<LoadingState?>
|
||||
@@ -38,86 +35,100 @@ class LoadAllGitLabMergeRequestTimelineViewModel(
|
||||
) : GitLabMergeRequestTimelineViewModel {
|
||||
|
||||
private val cs = parentCs.childScope(Dispatchers.Default)
|
||||
private var loadingRequestState = MutableStateFlow<Deferred<List<GitLabMergeRequestTimelineItemViewModel>>?>(null)
|
||||
private var loadingRequestState = MutableStateFlow<Flow<List<GitLabMergeRequestTimelineItemViewModel>>?>(null)
|
||||
|
||||
private val discussionsDataProvider = GitLabMergeRequestDiscussionsModelImpl(cs, connection, mr)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override val timelineLoadingFlow: Flow<LoadingState?> =
|
||||
loadingRequestState.transform {
|
||||
if (it == null) {
|
||||
loadingRequestState.transformLatest { itemsFlow ->
|
||||
if (itemsFlow == null) {
|
||||
emit(null)
|
||||
return@transform
|
||||
return@transformLatest
|
||||
}
|
||||
|
||||
emit(LoadingState.Loading)
|
||||
val result = try {
|
||||
val result = it.await()
|
||||
LoadingState.Result(result)
|
||||
|
||||
// so it's not a supervisor
|
||||
coroutineScope {
|
||||
itemsFlow.catch {
|
||||
emit(LoadingState.Error(it))
|
||||
}.collectLatest {
|
||||
emit(LoadingState.Result(it))
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
LoadingState.Error(e)
|
||||
}
|
||||
catch (ce: CancellationException) {
|
||||
throw ce
|
||||
}
|
||||
emit(result)
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
override fun startLoading() {
|
||||
loadingRequestState.update {
|
||||
it ?: requestItemsAsync()
|
||||
it ?: createItemsFlow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestItemsAsync(): Deferred<List<GitLabMergeRequestTimelineItemViewModel>> =
|
||||
cs.async {
|
||||
val api = connection.apiClient
|
||||
val project = connection.repo.repository
|
||||
private fun createItemsFlow(): Flow<List<GitLabMergeRequestTimelineItemViewModel>> {
|
||||
val api = connection.apiClient
|
||||
val project = connection.repo.repository
|
||||
|
||||
val result = ConcurrentSkipListSet(Comparator.comparing(GitLabMergeRequestTimelineItemViewModel::date))
|
||||
val discussionsFlow = discussionsDataProvider.userDiscussions.mapScoped { discussions ->
|
||||
val discussionsScope = this
|
||||
discussions.map {
|
||||
GitLabMergeRequestTimelineItemViewModel.Discussion(discussionsScope, it)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
val simpleEventsFlow: Flow<List<GitLabMergeRequestTimelineItemViewModel.Immutable>> =
|
||||
channelFlow {
|
||||
launch {
|
||||
api.loadAllMergeRequestDiscussions(project, mr).collect { discussions ->
|
||||
result.addAll(discussions.filter {
|
||||
it.notes.isNotEmpty()
|
||||
}.map {
|
||||
if (it.notes.first().system) {
|
||||
GitLabMergeRequestTimelineItemViewModel.SystemDiscussion(it)
|
||||
}
|
||||
else {
|
||||
GitLabMergeRequestTimelineItemViewModel.Discussion(cs, LoadedGitLabDiscussion(cs, connection, it))
|
||||
}
|
||||
})
|
||||
discussionsDataProvider.systemDiscussions.collect { discussions ->
|
||||
send(discussions.map { GitLabMergeRequestTimelineItemViewModel.SystemDiscussion(it) })
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
api.loadMergeRequestStateEvents(project, mr).body().let { events ->
|
||||
result.addAll(events.map { GitLabMergeRequestTimelineItemViewModel.StateEvent(it) })
|
||||
events.map { GitLabMergeRequestTimelineItemViewModel.StateEvent(it) }
|
||||
}.also {
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
api.loadMergeRequestStateEvents(project, mr).body().let { events ->
|
||||
events.map { GitLabMergeRequestTimelineItemViewModel.StateEvent(it) }
|
||||
}.also {
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
api.loadMergeRequestLabelEvents(project, mr).body().let { events ->
|
||||
result.addAll(events.map { GitLabMergeRequestTimelineItemViewModel.LabelEvent(it) })
|
||||
events.map { GitLabMergeRequestTimelineItemViewModel.LabelEvent(it) }
|
||||
}.also {
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
api.loadMergeRequestMilestoneEvents(project, mr).body().let { events ->
|
||||
result.addAll(events.map { GitLabMergeRequestTimelineItemViewModel.MilestoneEvent(it) })
|
||||
events.map { GitLabMergeRequestTimelineItemViewModel.MilestoneEvent(it) }
|
||||
}.also {
|
||||
send(it)
|
||||
}
|
||||
}
|
||||
}.join()
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
result.toList()
|
||||
|
||||
return combine(discussionsFlow, simpleEventsFlow) { discussions, simpleEvents ->
|
||||
TreeSet(Comparator.comparing(GitLabMergeRequestTimelineItemViewModel::date)).apply {
|
||||
addAll(simpleEvents)
|
||||
addAll(discussions)
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
override fun reset() {
|
||||
loadingRequestState.update {
|
||||
it?.cancel()
|
||||
null
|
||||
}
|
||||
loadingRequestState.value = null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user