[github] Check repo information before viewing pull requests

This allows to:
1. Check if user has access to repo
2. Follow repo rename redirects
This commit is contained in:
Ivan Semenov
2018-10-02 18:45:38 +03:00
parent 168d156800
commit 71cc00e245
5 changed files with 79 additions and 29 deletions
@@ -71,7 +71,7 @@ open class GithubOpenInBrowserActionGroup
private fun getDataFromPullRequest(project: Project, dataContext: DataContext): Pair<Set<GithubRepositoryPath>, Data>? {
val pullRequest = dataContext.getData(GithubPullRequestKeys.SELECTED_PULL_REQUEST) ?: return null
val serverPath = dataContext.getData(GithubPullRequestKeys.SERVER_PATH) ?: return null
val fullPath = dataContext.getData(GithubPullRequestKeys.FULL_PATH) ?: return null
val fullPath = dataContext.getData(GithubPullRequestKeys.REPO_DETAILS)?.fullPath ?: return null
val htmlUrl = pullRequest.pullRequestLinks?.htmlUrl ?: return null
return setOf(GithubRepositoryPath(serverPath, fullPath)) to Data.URL(project, htmlUrl)
@@ -4,12 +4,19 @@ package org.jetbrains.plugins.github
import com.intellij.icons.AllIcons
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.registry.Registry
import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
import org.jetbrains.plugins.github.api.GithubApiRequests
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
import org.jetbrains.plugins.github.pullrequest.GithubPullRequestsToolWindowManager
import org.jetbrains.plugins.github.util.GithubNotifications
import org.jetbrains.plugins.github.util.GithubUrlUtil
class GithubViewPullRequestsAction : AbstractGithubUrlGroupingAction("View Pull Requests", null, AllIcons.Vcs.Vendors.Github) {
override fun isEnabledAndVisible(e: AnActionEvent): Boolean {
@@ -22,6 +29,37 @@ class GithubViewPullRequestsAction : AbstractGithubUrlGroupingAction("View Pull
remote: GitRemote,
remoteUrl: String,
account: GithubAccount) {
project.service<GithubPullRequestsToolWindowManager>().showPullRequestsTab(repository, remote, remoteUrl, account)
val fullPath = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl)
if (fullPath == null) {
GithubNotifications.showError(project, "Invalid GitHub Repository URL", "$remoteUrl is not a GitHub repository.")
return
}
val requestExecutor = service<GithubApiRequestExecutorManager>().getExecutor(account, project) ?: return
val toolWindowManager = project.service<GithubPullRequestsToolWindowManager>()
if (toolWindowManager.showPullRequestsTabIfExists(repository, remote, remoteUrl, account)) return
object : Task.Backgroundable(project, "Loading GitHub Repository Information", true) {
lateinit var repoDetails: GithubRepoDetailed
override fun run(indicator: ProgressIndicator) {
val details = requestExecutor.execute(indicator, GithubApiRequests.Repos.get(account.server, fullPath.user, fullPath.repository))
?: throw IllegalArgumentException(
"Repository $fullPath does not exist at ${account.server} or you don't have access.")
repoDetails = details
indicator.checkCanceled()
}
override fun onSuccess() {
toolWindowManager.createPullRequestsTab(requestExecutor, repository, remote, remoteUrl, repoDetails, account)
toolWindowManager.showPullRequestsTabIfExists(repository, remote, remoteUrl, account)
}
override fun onThrowable(error: Throwable) {
GithubNotifications.showError(project, "Failed To Load Repository Information", error)
}
}.queue()
}
}
@@ -13,8 +13,8 @@ import com.intellij.ui.components.panels.Wrapper
import git4idea.commands.Git
import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
import org.jetbrains.plugins.github.api.GithubFullPath
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
import org.jetbrains.plugins.github.pullrequest.avatars.CachingGithubAvatarIconsProvider
@@ -24,13 +24,11 @@ import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsLoader
import org.jetbrains.plugins.github.pullrequest.ui.*
import org.jetbrains.plugins.github.util.CachingGithubUserAvatarLoader
import org.jetbrains.plugins.github.util.GithubImageResizer
import org.jetbrains.plugins.github.util.GithubUrlUtil
import javax.swing.JComponent
class GithubPullRequestsComponentFactory(private val project: Project,
private val progressManager: ProgressManager,
private val requestExecutorManager: GithubApiRequestExecutorManager,
private val git: Git,
private val uiSettings: GithubPullRequestsUISettings,
private val avatarLoader: CachingGithubUserAvatarLoader,
@@ -38,12 +36,13 @@ class GithubPullRequestsComponentFactory(private val project: Project,
private val actionManager: ActionManager,
private val autoPopupController: AutoPopupController) {
fun createComponent(repository: GitRepository, remote: GitRemote, remoteUrl: String, account: GithubAccount): JComponent? {
fun createComponent(requestExecutor: GithubApiRequestExecutor,
repository: GitRepository, remote: GitRemote,
repoDetails: GithubRepoDetailed,
account: GithubAccount): JComponent? {
val requestExecutor = requestExecutorManager.getExecutor(account, project) ?: return null
val repoPath = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl)!!
val listLoader = GithubPullRequestsLoader(progressManager, requestExecutor,
account.server, repoPath)
account.server, repoDetails.fullPath)
val selectionModel = GithubPullRequestsListSelectionModel()
val list = GithubPullRequestsListComponent(project, actionManager, autoPopupController,
selectionModel, listLoader,
@@ -64,8 +63,8 @@ class GithubPullRequestsComponentFactory(private val project: Project,
// disposed by content manager when tab is closed
val wrapper = WrappingComponent(splitter,
repository, remote, repoPath, account,
list, selectionModel, dataLoader)
repository, remote, repoDetails, account,
list,selectionModel, dataLoader)
Disposer.register(wrapper, Disposable {
Disposer.dispose(list)
Disposer.dispose(preview)
@@ -81,7 +80,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
private class WrappingComponent(wrapped: JComponent,
private val repository: GitRepository,
private val remote: GitRemote,
private val repoPath: GithubFullPath,
private val repoDetails: GithubRepoDetailed,
private val account: GithubAccount,
private val list: GithubPullRequestsListComponent,
private val selectionModel: GithubPullRequestsListSelectionModel,
@@ -95,7 +94,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
return when {
GithubPullRequestKeys.REPOSITORY.`is`(dataId) -> repository
GithubPullRequestKeys.REMOTE.`is`(dataId) -> remote
GithubPullRequestKeys.FULL_PATH.`is`(dataId) -> repoPath
GithubPullRequestKeys.REPO_DETAILS.`is`(dataId) -> repoDetails
GithubPullRequestKeys.SERVER_PATH.`is`(dataId) -> account.server
GithubPullRequestKeys.PULL_REQUESTS_LIST_COMPONENT.`is`(dataId) -> list
GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER.`is`(dataId) -> selectionModel.current?.let(dataLoader::getDataProvider)
@@ -18,6 +18,8 @@ import git4idea.repo.GitRepository
import git4idea.repo.GitRepositoryChangeListener
import git4idea.repo.GitRepositoryManager
import icons.GithubIcons
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
import org.jetbrains.plugins.github.authentication.accounts.AccountTokenChangedListener
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
import org.jetbrains.plugins.github.authentication.accounts.GithubAccountManager
@@ -36,13 +38,15 @@ class GithubPullRequestsToolWindowManager internal constructor(private val proje
private val accountManager: GithubAccountManager,
private val componentFactory: GithubPullRequestsComponentFactory) {
fun showPullRequestsTab(repository: GitRepository, remote: GitRemote, remoteUrl: String, account: GithubAccount) {
fun createPullRequestsTab(requestExecutor: GithubApiRequestExecutor,
repository: GitRepository, remote: GitRemote, remoteUrl: String,
repoDetails: GithubRepoDetailed,
account: GithubAccount) {
var toolWindow = toolWindowManager.getToolWindow(TOOL_WINDOW_ID)
val contentManager: ContentManager
val content: Content
if (toolWindow == null) {
val component = componentFactory.createComponent(repository, remote, remoteUrl, account) ?: return
val component = componentFactory.createComponent(requestExecutor, repository, remote, repoDetails, account) ?: return
toolWindow = toolWindowManager.registerToolWindow(TOOL_WINDOW_ID, true, ToolWindowAnchor.BOTTOM, project, true)
.apply {
@@ -56,22 +60,26 @@ class GithubPullRequestsToolWindowManager internal constructor(private val proje
}
})
content = createContent(contentManager, component, repository, remote, remoteUrl, account)
val content = createContent(contentManager, component, repository, remote, remoteUrl, account)
contentManager.addContent(content)
}
else {
contentManager = toolWindow.contentManager
val existingContent = contentManager.findContentByRemoteUrlInContent(remoteUrl)
content = if (existingContent != null) existingContent
else {
val component = componentFactory.createComponent(repository, remote, remoteUrl, account) ?: return
createContent(contentManager, component, repository, remote, remoteUrl, account)
val existingContent = contentManager.findContent(repository, remote, remoteUrl, account)
if (existingContent == null) {
val component = componentFactory.createComponent(requestExecutor, repository, remote, repoDetails, account) ?: return
val content = createContent(contentManager, component, repository, remote, remoteUrl, account)
contentManager.addContent(content)
}
}
}
contentManager.setSelectedContent(content, true)
fun showPullRequestsTabIfExists(repository: GitRepository, remote: GitRemote, remoteUrl: String, account: GithubAccount): Boolean {
val toolWindow = toolWindowManager.getToolWindow(TOOL_WINDOW_ID) ?: return false
val content = toolWindow.contentManager.findContent(repository, remote, remoteUrl, account) ?: return false
toolWindow.contentManager.setSelectedContent(content, true)
toolWindow.show { }
return true
}
private fun createContent(contentManager: ContentManager,
@@ -162,6 +170,11 @@ class GithubPullRequestsToolWindowManager internal constructor(private val proje
}
}
private fun ContentManager.findContentByRemoteUrlInContent(remoteUrl: String) =
contents.find { it.getUserData(REMOTE_URL_KEY) == remoteUrl }
private fun ContentManager.findContent(repository: GitRepository, remote: GitRemote, remoteUrl: String, account: GithubAccount) =
contents.find {
it.getUserData(REMOTE_URL_KEY) == remoteUrl &&
it.getUserData(REMOTE_KEY) == remote &&
it.getUserData(REPOSITORY_KEY) == repository &&
it.getUserData(ACCOUNT_KEY) == account
}
}
@@ -4,8 +4,8 @@ package org.jetbrains.plugins.github.pullrequest.action
import com.intellij.openapi.actionSystem.DataKey
import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import org.jetbrains.plugins.github.api.GithubFullPath
import org.jetbrains.plugins.github.api.GithubServerPath
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestsListComponent
@@ -24,7 +24,7 @@ object GithubPullRequestKeys {
@JvmStatic
val REMOTE = DataKey.create<GitRemote>("org.jetbrains.plugins.github.pullrequest.remote")
@JvmStatic
val FULL_PATH = DataKey.create<GithubFullPath>("org.jetbrains.plugins.github.pullrequest.remote.fullpath")
val REPO_DETAILS = DataKey.create<GithubRepoDetailed>("org.jetbrains.plugins.github.pullrequest.remote.repo.details")
@JvmStatic
val SERVER_PATH = DataKey.create<GithubServerPath>("org.jetbrains.plugins.github.pullrequest.server.path")
}