[github] Multiple accounts support for repo list loader

This commit is contained in:
Ivan Semenov
2018-04-17 15:14:40 +03:00
parent 60bbc1666f
commit e69eebc2f5
5 changed files with 84 additions and 27 deletions
+1
View File
@@ -25,6 +25,7 @@
<applicationService serviceImplementation="org.jetbrains.plugins.github.authentication.GithubAuthenticationManager"/>
<applicationService serviceImplementation="org.jetbrains.plugins.github.api.GithubApiTaskExecutor"/>
<applicationService serviceImplementation="org.jetbrains.plugins.github.authentication.accounts.GithubAccountInformationProvider"/>
<applicationService serviceImplementation="org.jetbrains.plugins.github.util.GithubGitHelper"/>
<projectService serviceInterface="org.jetbrains.plugins.github.util.GithubProjectSettings"
serviceImplementation="org.jetbrains.plugins.github.util.GithubProjectSettings"/>
<projectService serviceImplementation="org.jetbrains.plugins.github.authentication.accounts.GithubProjectDefaultAccountHolder"/>
@@ -2,6 +2,7 @@
package org.jetbrains.plugins.github.api
import com.intellij.openapi.application.invokeAndWaitIfNeed
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.ui.Messages
@@ -91,6 +92,11 @@ class GithubApiTaskExecutor(private val authenticationManager: GithubAuthenticat
return originalAuth.copyWithTwoFactorCode(code)
}
@JvmStatic
fun getInstance(): GithubApiTaskExecutor {
return service()
}
}
}
@@ -1,18 +1,28 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.github.authentication
import com.intellij.openapi.project.Project
import git4idea.DialogManager
import com.intellij.openapi.components.service
import org.jetbrains.annotations.CalledInAny
import org.jetbrains.annotations.CalledInAwt
import org.jetbrains.annotations.TestOnly
import org.jetbrains.plugins.github.api.GithubServerPath
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
import org.jetbrains.plugins.github.authentication.accounts.GithubAccountManager
import org.jetbrains.plugins.github.exceptions.GithubAuthenticationException
import org.jetbrains.plugins.github.authentication.ui.GithubLoginDialog
/**
* Entry point for interactions with Github authentication subsystem
*/
class GithubAuthenticationManager internal constructor(private val accountManager: GithubAccountManager) {
@CalledInAny
fun hasAccounts() = accountManager.accounts.isNotEmpty()
@CalledInAny
fun getAccounts(): Set<GithubAccount> = accountManager.accounts
@CalledInAny
internal fun getTokenForAccount(account: GithubAccount): String {
val token = accountManager.getTokenForAccount(account)
@@ -20,6 +30,22 @@ class GithubAuthenticationManager internal constructor(private val accountManage
else return token
}
@CalledInAwt
fun requestNewAccount(project: Project): GithubAccount? {
fun isAccountUnique(name: String, server: GithubServerPath) =
accountManager.accounts.none { it.name == name && it.server == server }
val dialog = GithubLoginDialog(project, ::isAccountUnique)
DialogManager.show(dialog)
if (dialog.isOK) {
val account = GithubAccount(dialog.getLogin(), dialog.getServer())
accountManager.accounts += account
accountManager.updateAccountToken(account, dialog.getToken())
return account
}
return null
}
@TestOnly
fun registerAccount(name: String, host: String, token: String): GithubAccount {
val account = GithubAccount(name, GithubServerPath.from(host))
@@ -5,64 +5,69 @@ import com.intellij.dvcs.hosting.RepositoryListLoader;
import com.intellij.dvcs.hosting.RepositoryListLoadingException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import git4idea.DialogManager;
import git4idea.remote.GitRepositoryHostingService;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.github.api.GithubApiTaskExecutor;
import org.jetbrains.plugins.github.api.GithubApiUtil;
import org.jetbrains.plugins.github.api.data.GithubRepo;
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
import org.jetbrains.plugins.github.util.*;
import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager;
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount;
import org.jetbrains.plugins.github.util.GithubGitHelper;
import org.jetbrains.plugins.github.util.GithubUtil;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class GithubRepositoryHostingService extends GitRepositoryHostingService {
@NotNull private final GithubAuthenticationManager myAuthenticationManager;
@NotNull private final GithubGitHelper myGitHelper;
public GithubRepositoryHostingService(@NotNull GithubAuthenticationManager manager,
@NotNull GithubGitHelper gitHelper) {
myAuthenticationManager = manager;
myGitHelper = gitHelper;
}
@NotNull
@Override
public String getServiceDisplayName() {
return "GitHub";
return GithubUtil.SERVICE_DISPLAY_NAME;
}
@Override
@NotNull
public RepositoryListLoader getRepositoryListLoader(@NotNull Project project) {
return new RepositoryListLoader() {
@NotNull private final GithubAuthDataHolder myAuthDataHolder = GithubAuthDataHolder.createFromSettings();
@Override
public boolean isEnabled() {
return AuthLevel.LOGGED.accepts(myAuthDataHolder.getAuthData());
return myAuthenticationManager.hasAccounts();
}
@Override
public boolean enable() {
GithubAuthData currentAuthData = myAuthDataHolder.getAuthData();
myAuthDataHolder.runTransaction(currentAuthData, () -> {
GithubLoginDialog dialog = new GithubLoginDialog(project, currentAuthData, AuthLevel.LOGGED);
DialogManager.show(dialog);
if (dialog.isOK()) {
GithubAuthData authData = dialog.getAuthData();
GithubSettings.getInstance().setAuthData(authData, dialog.isSavePasswordSelected());
return authData;
}
return currentAuthData;
});
return isEnabled();
return myAuthenticationManager.requestNewAccount(project) != null;
}
@NotNull
@Override
public List<String> getAvailableRepositories(@NotNull ProgressIndicator progressIndicator) throws RepositoryListLoadingException {
try {
return GithubUtil.runTask(project, myAuthDataHolder, progressIndicator, connection -> GithubApiUtil.getAvailableRepos(connection))
.stream()
.sorted(Comparator.comparing(GithubRepo::getUserName).thenComparing(GithubRepo::getName))
.map(repo -> GithubUrlUtil
.getCloneUrl(GithubUrlUtil.getGitHostWithoutProtocol(myAuthDataHolder.getAuthData().getHost()),
repo.getUserName(),
repo.getName()))
.collect(Collectors.toList());
List<String> urls = new ArrayList<>();
for (GithubAccount account : myAuthenticationManager.getAccounts()) {
urls.addAll(
GithubApiTaskExecutor.getInstance().execute(progressIndicator, account,
connection -> GithubApiUtil.getAvailableRepos(connection))
.stream()
.sorted(Comparator.comparing(GithubRepo::getUserName).thenComparing(GithubRepo::getName))
.map(repo -> myGitHelper.getRemoteUrl(account.getServer(),
repo.getUserName(),
repo.getName()))
.collect(Collectors.toList())
);
}
return urls;
}
catch (Exception e) {
throw new RepositoryListLoadingException("Error connecting to Github", e);
@@ -0,0 +1,19 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.github.util
import org.jetbrains.plugins.github.api.GithubServerPath
/**
* Utilities for Github-Git interactions
*/
class GithubGitHelper(private val githubSettings: GithubSettings) {
fun getRemoteUrl(server: GithubServerPath, user: String, repo: String): String {
return if (githubSettings.isCloneGitUsingSsh) {
"git@${server.host}:${server.suffix?.substring(1).orEmpty()}/$user/$repo.git"
}
else {
"https://${server.host}${server.suffix.orEmpty()}/$user/$repo.git"
}
}
}