mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Move to GitHub API v3, refactor. IDEA-85769
* Introduce getRequest() and postRequest() methods as entry points to do a query to the API instead of doREST, which requires releasing an HttpMethod and so on. * Return parsed JsonElement from these methods, since Github API returns the response in JSON format. * Post request in JSON format as well. Get-methods are not called with parameters yet, so don't bother for them. * Use these methods from GithubCreateGistAction which already uses API v3. * Introduce getApiUrl to return the correct API url for github as well as for enterprise installations. * Introduce GithubUser to encapsulate information about a user. Currently it works only for the current user, and stores only its plan to know if private repositories are allowed for the person. * Rewrite RepositoryInfo to be a structured holder of the information about a repository, without working with DOM elements and so on. Rename some methods to make their return value more obvious. * Add @Nullable, @NotNull, javadocs, code style fixes. * In the GithubShareAction move initial pushing to the background to avoid the modality lock with the dialog asking for the passphrase. This fixes IDEA-86645.
This commit is contained in:
@@ -50,7 +50,7 @@ public class GithubCheckoutProvider implements CheckoutProvider {
|
||||
Collections.sort(availableRepos, new Comparator<RepositoryInfo>() {
|
||||
@Override
|
||||
public int compare(final RepositoryInfo r1, final RepositoryInfo r2) {
|
||||
final int comparedOwners = r1.getOwner().compareTo(r2.getOwner());
|
||||
final int comparedOwners = r1.getOwnerName().compareTo(r2.getOwnerName());
|
||||
return comparedOwners != 0 ? comparedOwners : r1.getName().compareTo(r2.getName());
|
||||
}
|
||||
});
|
||||
@@ -58,7 +58,7 @@ public class GithubCheckoutProvider implements CheckoutProvider {
|
||||
final GitCloneDialog dialog = new GitCloneDialog(project);
|
||||
// Add predefined repositories to history
|
||||
for (int i = availableRepos.size() - 1; i>=0; i--){
|
||||
dialog.prependToHistory(availableRepos.get(i).getUrl());
|
||||
dialog.prependToHistory(availableRepos.get(i).getCloneUrl());
|
||||
}
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
|
||||
@@ -17,8 +17,6 @@ package org.jetbrains.plugins.github;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.intellij.ide.BrowserUtil;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
@@ -34,9 +32,6 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.Notificator;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.httpclient.methods.StringRequestEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GitHubCreateGistDialog;
|
||||
@@ -130,41 +125,7 @@ public class GithubCreateGistAction extends DumbAwareAction {
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Using GitHub Gist API v3: http://developer.github.com/v3/gists/
|
||||
final HttpClient client = anonymous ? GithubUtil.getHttpClient(null, null) : GithubUtil.getHttpClient(settings.getLogin(), password);
|
||||
final PostMethod method = new PostMethod("https://api.github.com/gists");
|
||||
|
||||
String request = prepareJsonRequest(description, isPrivate, text, file);
|
||||
|
||||
String response;
|
||||
try {
|
||||
method.setRequestEntity(new StringRequestEntity(request, "application/json", "UTF-8"));
|
||||
client.executeMethod(method);
|
||||
response = method.getResponseBodyAsString();
|
||||
}
|
||||
catch (IOException e1) {
|
||||
showError(project, "Failed to create gist", "", null, e1);
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
method.releaseConnection();
|
||||
}
|
||||
|
||||
JsonObject jsonResponse;
|
||||
try {
|
||||
jsonResponse = new JsonParser().parse(response).getAsJsonObject();
|
||||
}
|
||||
catch (JsonSyntaxException jse) {
|
||||
showError(project, "Couldn't parse GitHub response", "", response, jse);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonElement htmlUrl = jsonResponse.get("html_url");
|
||||
if (htmlUrl == null) {
|
||||
showError(project, "Invalid GitHub response", "No html_url property", response, null);
|
||||
return;
|
||||
}
|
||||
url.set(htmlUrl.getAsString());
|
||||
url.set(createGist(project, settings.getLogin(), password, anonymous, text, isPrivate, file, description));
|
||||
}
|
||||
}, "Communicating With GitHub", false, project);
|
||||
|
||||
@@ -188,6 +149,40 @@ public class GithubCreateGistAction extends DumbAwareAction {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String createGist(@NotNull Project project, @Nullable String login, @Nullable String password, boolean anonymous,
|
||||
@NotNull String text, boolean isPrivate, @NotNull VirtualFile file, @NotNull String description) {
|
||||
if (anonymous) {
|
||||
login = null;
|
||||
password = null;
|
||||
}
|
||||
String requestBody = prepareJsonRequest(description, isPrivate, text, file);
|
||||
try {
|
||||
JsonElement jsonElement = GithubUtil.postRequest("https://api.github.com", login, password, "/gists", requestBody);
|
||||
if (jsonElement == null) {
|
||||
LOG.info("Null JSON response returned by GitHub");
|
||||
showError(project, "Failed to create gist", "Empty JSON response returned by GitHub", null, null);
|
||||
return null;
|
||||
}
|
||||
if (!jsonElement.isJsonObject()) {
|
||||
LOG.error(String.format("Unexpected JSON result format: %s", jsonElement));
|
||||
return null;
|
||||
}
|
||||
JsonElement htmlUrl = jsonElement.getAsJsonObject().get("html_url");
|
||||
if (htmlUrl == null) {
|
||||
LOG.info("Invalid JSON response: " + jsonElement);
|
||||
showError(project, "Invalid GitHub response", "No html_url property", jsonElement.toString(), null);
|
||||
return null;
|
||||
}
|
||||
return htmlUrl.getAsString();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info("Exception when creating a Gist", e);
|
||||
showError(project, "Failed to create gist", "", null, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void showError(@NotNull Project project, @NotNull String title, @NotNull String content,
|
||||
@Nullable String details, @Nullable Exception e) {
|
||||
Notificator.getInstance(project).notifyError(title, content);
|
||||
|
||||
@@ -139,7 +139,7 @@ public class GithubRebaseAction extends DumbAwareAction {
|
||||
return;
|
||||
}
|
||||
|
||||
final String parent = repositoryInfo.getParent();
|
||||
final String parent = repositoryInfo.getParentName();
|
||||
LOG.assertTrue(parent != null, "Parent repository not found!");
|
||||
final String parentRepoSuffix = parent + ".git";
|
||||
final String parentRepoUrl = "git://github.com/" + parentRepoSuffix;
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author oleg
|
||||
@@ -43,13 +42,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
)
|
||||
public class GithubSettings implements PersistentStateComponent<Element> {
|
||||
|
||||
public static final String DEFAULT_GITHUB_HOST = "github.com";
|
||||
|
||||
private static final String GITHUB_SETTINGS_TAG = "GithubSettings";
|
||||
private static final String LOGIN = "Login";
|
||||
private static final String HOST = "Host";
|
||||
private static final String ANONIMOUS_GIST = "Anonymous";
|
||||
private static final String OPEN_IN_BROWSER_GIST = "OpenInBrowser";
|
||||
private static final String PRIVATE_GIST = "Private";
|
||||
private static final String GITHUB = "github.com";
|
||||
private static final String GITHUB_SETTINGS_PASSWORD_KEY = "GITHUB_SETTINGS_PASSWORD_KEY";
|
||||
|
||||
private String myLogin;
|
||||
@@ -117,7 +117,7 @@ public class GithubSettings implements PersistentStateComponent<Element> {
|
||||
return myLogin != null ? myLogin : "";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public String getPassword() {
|
||||
LOG.assertTrue(!ProgressManager.getInstance().hasProgressIndicator(), "Password should not be accessed under modal progress");
|
||||
String password;
|
||||
@@ -154,7 +154,7 @@ public class GithubSettings implements PersistentStateComponent<Element> {
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return myHost != null ? myHost : GITHUB;
|
||||
return myHost != null ? myHost : DEFAULT_GITHUB_HOST;
|
||||
}
|
||||
|
||||
public boolean isAnonymous() {
|
||||
@@ -187,7 +187,7 @@ public class GithubSettings implements PersistentStateComponent<Element> {
|
||||
}
|
||||
|
||||
public void setHost(final String host) {
|
||||
myHost = host != null ? host : GITHUB;
|
||||
myHost = host != null ? host : DEFAULT_GITHUB_HOST;
|
||||
}
|
||||
|
||||
public void setAnonymousGist(final boolean anonymousGist) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
@@ -26,11 +28,11 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager;
|
||||
@@ -53,6 +55,7 @@ import git4idea.repo.GitRepository;
|
||||
import git4idea.repo.GitRepositoryManager;
|
||||
import git4idea.util.GitFileUtils;
|
||||
import git4idea.util.GitUIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.github.ui.GithubShareDialog;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -83,7 +86,7 @@ public class GithubShareAction extends DumbAwareAction {
|
||||
@Override
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
final Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
if (!GithubUtil.testGitExecutable(project)){
|
||||
if (project == null || !GithubUtil.testGitExecutable(project)){
|
||||
return;
|
||||
}
|
||||
final VirtualFile root = project.getBaseDir();
|
||||
@@ -136,22 +139,49 @@ public class GithubShareAction extends DumbAwareAction {
|
||||
final String description = shareDialog.getDescription();
|
||||
try {
|
||||
LOG.info("Creating GitHub repository");
|
||||
final String escapedDescription = JDOMUtil.escapeText(description, true, true).replace("&#", "%");
|
||||
GithubUtil.doREST(settings.getHost(), settings.getLogin(), settings.getPassword(),
|
||||
"/repos/create?name=" + name + "&public=" + (isPrivate ? "0" : "1") + "&description=" + escapedDescription, true).releaseConnection();
|
||||
LOG.info("Successfully created GitHub repository");
|
||||
boolean repositoryCreated =
|
||||
createGithubRepository(settings.getHost(), settings.getLogin(), settings.getPassword(), name, description, isPrivate);
|
||||
if (repositoryCreated) {
|
||||
LOG.info("Successfully created GitHub repository");
|
||||
}
|
||||
else {
|
||||
Messages.showErrorDialog(project, "Failed to create new GitHub repository", "Create GitHub Repository");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (final Exception e1) {
|
||||
Messages.showErrorDialog(e1.getMessage(), "Failed to create new GitHub repository");
|
||||
return;
|
||||
}
|
||||
if (bindToGithub(project, root, gitDetected, settings.getLogin(), name)) {
|
||||
Notifications.Bus.notify(new Notification("github", "Success", "Successfully created project ''" + name + "'' on github",
|
||||
NotificationType.INFORMATION));
|
||||
}
|
||||
|
||||
bindToGithub(project, root, gitDetected, settings.getLogin(), name);
|
||||
}
|
||||
|
||||
private boolean bindToGithub(final Project project, final VirtualFile root, final boolean gitDetected, final String login, String name) {
|
||||
private static boolean createGithubRepository(@NotNull String host, @NotNull String login, @NotNull String password, @NotNull String name,
|
||||
@NotNull String description, boolean aPrivate) throws IOException {
|
||||
String path = "/user/repos";
|
||||
String requestBody = prepareRequest(name, description, aPrivate);
|
||||
JsonElement result = GithubUtil.postRequest(host, login, password, path, requestBody);
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
if (!result.isJsonObject()) {
|
||||
LOG.error(String.format("Unexpected JSON result format: %s", result));
|
||||
return false;
|
||||
}
|
||||
return result.getAsJsonObject().has("url");
|
||||
}
|
||||
|
||||
private static String prepareRequest(String name, String description, boolean isPrivate) {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("name", name);
|
||||
json.addProperty("description", description);
|
||||
json.addProperty("public", Boolean.toString(!isPrivate));
|
||||
return json.toString();
|
||||
|
||||
}
|
||||
|
||||
private void bindToGithub(final Project project, final VirtualFile root, boolean gitDetected, final String login, final String name) {
|
||||
LOG.info("Binding local project with GitHub");
|
||||
// creating empty git repo if git isnot initialized
|
||||
if (!gitDetected) {
|
||||
@@ -162,7 +192,7 @@ public class GithubShareAction extends DumbAwareAction {
|
||||
if (!h.errors().isEmpty()) {
|
||||
GitUIUtil.showOperationErrors(project, h.errors(), "git init");
|
||||
LOG.info("Failed to create empty git repo: " + h.errors());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
final ProgressManager manager = ProgressManager.getInstance();
|
||||
manager.runProcessWithProgressSynchronously(new Runnable() {
|
||||
@@ -175,7 +205,7 @@ public class GithubShareAction extends DumbAwareAction {
|
||||
|
||||
// In this case we should create sample commit for binding project
|
||||
if (!performFirstCommitIfRequired(project, root)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
//git remote add origin git@github.com:login/name.git
|
||||
@@ -188,36 +218,40 @@ public class GithubShareAction extends DumbAwareAction {
|
||||
addRemoteHandler.run();
|
||||
if (addRemoteHandler.getExitCode() != 0) {
|
||||
Messages.showErrorDialog("Failed to add GitHub repository as remote", "Failed to add GitHub repository as remote");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (VcsException e) {
|
||||
Messages.showErrorDialog(e.getMessage(), "Failed to add GitHub repository as remote");
|
||||
LOG.info("Failed to add GitHub as remote: " + e.getMessage());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
//git push origin master
|
||||
final ProgressManager manager = ProgressManager.getInstance();
|
||||
|
||||
final ArrayList<VcsException> errors = new ArrayList<VcsException>();
|
||||
manager.runProcessWithProgressSynchronously(new Runnable() {
|
||||
public void run() {
|
||||
final ProgressIndicator progressIndicator = manager.getProgressIndicator();
|
||||
if (progressIndicator != null){
|
||||
progressIndicator.setText("Pushing to GitHub");
|
||||
}
|
||||
final GitLineHandler gitPushHandler = new GitLineHandler(project, root, GitCommand.PUSH);
|
||||
gitPushHandler.addParameters("-u", "origin", "master");
|
||||
GitPushUtils.trackPushRejectedAsError(gitPushHandler, "Rejected push (" + root.getPresentableUrl() + "): ");
|
||||
errors.addAll(GitHandlerUtil.doSynchronouslyWithExceptions(gitPushHandler));
|
||||
new Task.Backgroundable(project, "Pushing to GitHub", false) {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
final GitLineHandler gitPushHandler = new GitLineHandler(project, root, GitCommand.PUSH);
|
||||
gitPushHandler.addParameters("-u", "origin", "master");
|
||||
GitPushUtils.trackPushRejectedAsError(gitPushHandler, "Rejected push (" + root.getPresentableUrl() + "): ");
|
||||
errors.addAll(GitHandlerUtil.doSynchronouslyWithExceptions(gitPushHandler));
|
||||
if (!errors.isEmpty()) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GitUIUtil.showOperationErrors(project, errors, GitBundle.getString("push.active.pushing"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, GitBundle.getString("push.active.pushing"), false, project);
|
||||
if (!errors.isEmpty()) {
|
||||
GitUIUtil.showOperationErrors(project, errors, GitBundle.getString("push.active.pushing"));
|
||||
}
|
||||
// refresh vcs manually
|
||||
RefreshAction.doRefresh(project);
|
||||
return true;
|
||||
else {
|
||||
RefreshAction.doRefresh(project);
|
||||
Notifications.Bus.notify(new Notification("github", "Success", "Successfully created project ''" + name + "'' on github",
|
||||
NotificationType.INFORMATION));
|
||||
}
|
||||
}
|
||||
}.queue();
|
||||
}
|
||||
|
||||
private boolean performFirstCommitIfRequired(final Project project, final VirtualFile root) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Information about a user on GitHub.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
class GithubUser {
|
||||
|
||||
enum Plan {
|
||||
FREE,
|
||||
MICRO,
|
||||
SMALL,
|
||||
MEDIUM,
|
||||
BRONZE,
|
||||
SILVER,
|
||||
GOLD,
|
||||
PLATINUM;
|
||||
|
||||
public boolean isPrivateRepoAllowed() {
|
||||
return this != FREE;
|
||||
}
|
||||
|
||||
public static Plan fromString(String name) {
|
||||
for (Plan plan : values()) {
|
||||
if (plan.name().equalsIgnoreCase(name)) {
|
||||
return plan;
|
||||
}
|
||||
}
|
||||
return defaultPlan();
|
||||
}
|
||||
|
||||
private static Plan defaultPlan() {
|
||||
return FREE;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull private final Plan myPlan;
|
||||
|
||||
GithubUser(@NotNull Plan plan) {
|
||||
myPlan = plan;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Plan getPlan() {
|
||||
return myPlan;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -37,35 +41,41 @@ import org.apache.commons.httpclient.UsernamePasswordCredentials;
|
||||
import org.apache.commons.httpclient.auth.AuthScope;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.input.SAXBuilder;
|
||||
import org.apache.commons.httpclient.methods.StringRequestEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.github.ui.GithubLoginDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Various utility methods for the GutHub plugin.
|
||||
*
|
||||
* @author oleg
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GithubUtil {
|
||||
private static final String API_URL = "/api/v2/xml";
|
||||
private static final Logger LOG = Logger.getInstance(GithubUtil.class.getName());
|
||||
|
||||
public static final Icon GITHUB_ICON = IconLoader.getIcon("/org/jetbrains/plugins/github/github_icon.png");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(GithubUtil.class.getName());
|
||||
|
||||
/**
|
||||
* @deprecated The host may be defined in different formats. Use {@link #getApiUrl(String)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String getHttpsUrl() {
|
||||
return "https://" + GithubSettings.getInstance().getHost();
|
||||
}
|
||||
|
||||
public static String getHostByUrl(final String url) {
|
||||
return url.startsWith("https://") ? url.substring(8) : url.startsWith("http://") ? url.substring(7) : url.startsWith("git@") ? url.substring(4) : url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> T accessToGithubWithModalProgress(final Project project, final Computable<T> computable) {
|
||||
final Ref<T> result = new Ref<T>();
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
@@ -76,6 +86,10 @@ public class GithubUtil {
|
||||
return result.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO Use background progress
|
||||
*/
|
||||
@Deprecated
|
||||
public static void accessToGithubWithModalProgress(final Project project, final Runnable runnable) {
|
||||
ProgressManager.getInstance().run(new Task.Modal(project, "Access to GitHub", true) {
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
@@ -84,38 +98,113 @@ public class GithubUtil {
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean testConnection(final String url, final String login, final String password) {
|
||||
@Nullable
|
||||
public static JsonElement getRequest(@NotNull String host, @NotNull String login, @NotNull String password,
|
||||
@NotNull String path) throws IOException {
|
||||
return request(host, login, password, path, null, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JsonElement postRequest(@NotNull String host, @Nullable String login, @Nullable String password,
|
||||
@NotNull String path, @Nullable String requestBody) throws IOException {
|
||||
return request(host, login, password, path, requestBody, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JsonElement request(@NotNull String host, @Nullable String login, @Nullable String password,
|
||||
@NotNull String path, @Nullable String requestBody, boolean post) throws IOException {
|
||||
HttpMethod method = null;
|
||||
try {
|
||||
method = doREST(url, login, password, "/user/show/" + login, false);
|
||||
final InputStream stream = method.getResponseBodyAsStream();
|
||||
final Element element = new SAXBuilder(false).build(stream).getRootElement();
|
||||
if ("error".equals(element.getName())){
|
||||
return false;
|
||||
method = doREST(host, login, password, path, requestBody, post);
|
||||
String resp = method.getResponseBodyAsString();
|
||||
if (resp == null) {
|
||||
LOG.info(String.format("Unexpectedly empty response: %s", resp));
|
||||
return null;
|
||||
}
|
||||
// In case if authentification was successful we should see some extra fields
|
||||
return element.getChild("total-private-repo-count") != null;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore
|
||||
return parseResponse(resp);
|
||||
}
|
||||
finally {
|
||||
if (method!=null) {
|
||||
if (method != null) {
|
||||
method.releaseConnection();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static HttpMethod doREST(final String url, final String login, final String password, final String request, final boolean post) throws Exception {
|
||||
@NotNull
|
||||
private static HttpMethod doREST(@NotNull String host, @Nullable String login, @Nullable String password, @NotNull String path,
|
||||
@Nullable String requestBody, final boolean post) throws IOException {
|
||||
final HttpClient client = getHttpClient(login, password);
|
||||
final String uri = "https://" + getHostByUrl(url) + API_URL + request;
|
||||
final HttpMethod method = post ? new PostMethod(uri) : new GetMethod(uri);
|
||||
final String uri = getApiUrl(host) + path;
|
||||
final HttpMethod method;
|
||||
if (post) {
|
||||
method = new PostMethod(uri);
|
||||
if (requestBody != null) {
|
||||
((PostMethod)method).setRequestEntity(new StringRequestEntity(requestBody, "application/json", "UTF-8"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
method = new GetMethod(uri);
|
||||
}
|
||||
|
||||
client.executeMethod(method);
|
||||
return method;
|
||||
}
|
||||
|
||||
public static HttpClient getHttpClient(@Nullable final String login, @Nullable final String password) {
|
||||
@NotNull
|
||||
private static String removeProtocolPrefix(final String url) {
|
||||
if (url.startsWith("https://")) {
|
||||
return url.substring(8);
|
||||
}
|
||||
else if (url.startsWith("http://")) {
|
||||
return url.substring(7);
|
||||
}
|
||||
else if (url.startsWith("git@")) {
|
||||
return url.substring(4);
|
||||
}
|
||||
else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getApiUrl(@NotNull String urlFromSettings) {
|
||||
return "https://" + getApiUrlWithoutProtocol(urlFromSettings);
|
||||
}
|
||||
|
||||
/*
|
||||
All API access is over HTTPS, and accessed from the api.github.com domain
|
||||
(or through yourdomain.com/api/v3/ for enterprise).
|
||||
http://developer.github.com/v3/
|
||||
*/
|
||||
@NotNull
|
||||
private static String getApiUrlWithoutProtocol(String urlFromSettings) {
|
||||
String url = removeTrailingSlash(removeProtocolPrefix(urlFromSettings));
|
||||
final String API_PREFIX = "api.";
|
||||
final String ENTERPRISE_API_SUFFIX = "/api/v3";
|
||||
|
||||
if (url.equals(GithubSettings.DEFAULT_GITHUB_HOST)) {
|
||||
return API_PREFIX + url;
|
||||
}
|
||||
else if (url.equals(API_PREFIX + GithubSettings.DEFAULT_GITHUB_HOST)) {
|
||||
return url;
|
||||
}
|
||||
else if (url.endsWith(ENTERPRISE_API_SUFFIX)) {
|
||||
return url;
|
||||
}
|
||||
else {
|
||||
return url + ENTERPRISE_API_SUFFIX;
|
||||
}
|
||||
}
|
||||
|
||||
private static String removeTrailingSlash(String s) {
|
||||
if (s.endsWith("/")) {
|
||||
return s.substring(0, s.length() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static HttpClient getHttpClient(@Nullable final String login, @Nullable final String password) {
|
||||
final HttpClient client = new HttpClient();
|
||||
client.getParams().setContentCharset("UTF-8");
|
||||
// Configure proxySettings if it is required
|
||||
@@ -135,87 +224,127 @@ public class GithubUtil {
|
||||
return client;
|
||||
}
|
||||
|
||||
public static List<RepositoryInfo> getAvailableRepos(final String url, final String login, final String password, final boolean ownOnly) {
|
||||
HttpMethod method = null;
|
||||
try {
|
||||
final String request = (ownOnly ? "/repos/show/" : "/repos/watched/") + login;
|
||||
method = doREST(url, login, password, request, false);
|
||||
final InputStream stream = method.getResponseBodyAsStream();
|
||||
final Element element = new SAXBuilder(false).build(stream).getRootElement();
|
||||
if ("error".equals(element.getName())){
|
||||
LOG.warn("Got error element by request: " + request);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List repositories = element.getChildren();
|
||||
final List<RepositoryInfo> result = new ArrayList<RepositoryInfo>();
|
||||
for (int i = 0; i < repositories.size(); i++) {
|
||||
final Element repo = (Element)repositories.get(i);
|
||||
result.add(new RepositoryInfo(repo));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
finally {
|
||||
if (method != null){
|
||||
method.releaseConnection();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
private static boolean testConnection(final String url, final String login, final String password) {
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
return user != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static GithubUser retrieveCurrentUserInfo(@NotNull String url, @NotNull String login, @NotNull String password) {
|
||||
try {
|
||||
JsonElement result = getRequest(url, login, password, "/user");
|
||||
return parseUserInfo(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.info(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static RepositoryInfo getDetailedRepoInfo(final String url, final String login, final String password, final String owner, final String name) {
|
||||
HttpMethod method = null;
|
||||
private static GithubUser parseUserInfo(@Nullable JsonElement result) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
if (!result.isJsonObject()) {
|
||||
LOG.error(String.format("Unexpected JSON result format: %s", result));
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonObject obj = (JsonObject)result;
|
||||
if (!obj.has("plan")) {
|
||||
return null;
|
||||
}
|
||||
GithubUser.Plan plan = parsePlan(obj.get("plan"));
|
||||
return new GithubUser(plan);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static GithubUser.Plan parsePlan(JsonElement plan) {
|
||||
if (!plan.isJsonObject()) {
|
||||
return GithubUser.Plan.FREE;
|
||||
}
|
||||
return GithubUser.Plan.fromString(plan.getAsJsonObject().get("name").getAsString());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JsonElement parseResponse(@NotNull String githubResponse) throws IOException {
|
||||
try {
|
||||
final String request = "/repos/show/" + owner + "/" + name;
|
||||
method = doREST(url, login, password, request, false);
|
||||
final InputStream stream = method.getResponseBodyAsStream();
|
||||
final Element element = new SAXBuilder(false).build(stream).getRootElement();
|
||||
if ("error".equals(element.getName())){
|
||||
LOG.warn("Got error element by request: " + request);
|
||||
return new JsonParser().parse(githubResponse);
|
||||
}
|
||||
catch (JsonSyntaxException jse) {
|
||||
throw new IOException(String.format("Couldn't parse GitHub response:%n%s", githubResponse), jse);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> getAvailableRepos(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
boolean ownOnly) {
|
||||
final String request = (ownOnly ? "/user/repos" : "/user/watched");
|
||||
try {
|
||||
JsonElement result = getRequest(url, login, password, request);
|
||||
if (result == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return parseRepositoryInfos(result);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<RepositoryInfo> parseRepositoryInfos(@NotNull JsonElement result) {
|
||||
if (!result.isJsonArray()) {
|
||||
LOG.assertTrue(result.isJsonObject(), String.format("Unexpected JSON result format: %s", result));
|
||||
return Collections.singletonList(parseSingleRepositoryInfo(result.getAsJsonObject()));
|
||||
}
|
||||
|
||||
List<RepositoryInfo> repositories = new ArrayList<RepositoryInfo>();
|
||||
for (JsonElement element : result.getAsJsonArray()) {
|
||||
LOG.assertTrue(element.isJsonObject(),
|
||||
String.format("This element should be a JsonObject: %s%nTotal JSON response: %n%s", element, result));
|
||||
repositories.add(parseSingleRepositoryInfo(element.getAsJsonObject()));
|
||||
}
|
||||
return repositories;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RepositoryInfo parseSingleRepositoryInfo(@NotNull JsonObject result) {
|
||||
String name = result.get("name").getAsString();
|
||||
String cloneUrl = result.get("clone_url").getAsString();
|
||||
String ownerName = result.get("owner").getAsJsonObject().get("login").getAsString();
|
||||
String parentName = result.has("parent") ? result.get("parent").getAsJsonObject().get("full_name").getAsString(): null;
|
||||
boolean fork = result.get("fork").getAsBoolean();
|
||||
return new RepositoryInfo(name, cloneUrl, ownerName, parentName, fork);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static RepositoryInfo getDetailedRepoInfo(@NotNull String url, @NotNull String login, @NotNull String password,
|
||||
@NotNull String owner, @NotNull String name) {
|
||||
try {
|
||||
final String request = "/repos/" + owner + "/" + name;
|
||||
JsonElement jsonObject = getRequest(url, login, password, request);
|
||||
if (jsonObject == null) {
|
||||
LOG.info(String.format("Information about repository is unavailable. Owner: %s, Name: %s", owner, name));
|
||||
return null;
|
||||
}
|
||||
return (new RepositoryInfo(element));
|
||||
return parseSingleRepositoryInfo(jsonObject.getAsJsonObject());
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
catch (IOException e) {
|
||||
LOG.info(String.format("Exception was thrown when trying to retrieve information about repository. Owner: %s, Name: %s",
|
||||
owner, name));
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
if (method != null){
|
||||
method.releaseConnection();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isPrivateRepoAllowed(final String url, final String login, final String password) {
|
||||
HttpMethod method = null;
|
||||
try {
|
||||
final String request = "/user/show/" + login;
|
||||
method = doREST(url, login, password, request, false);
|
||||
final InputStream stream = method.getResponseBodyAsStream();
|
||||
final Element element = new SAXBuilder(false).build(stream).getRootElement();
|
||||
if ("error".equals(element.getName())){
|
||||
LOG.warn("Got error element by request: " + request);
|
||||
return false;
|
||||
}
|
||||
final Element plan = element.getChild("plan");
|
||||
assert plan != null : "Authentification failed";
|
||||
final String privateRepos = plan.getChildText("private-repos");
|
||||
return privateRepos != null && Integer.valueOf(privateRepos) > 0;
|
||||
GithubUser user = retrieveCurrentUserInfo(url, login, password);
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
finally {
|
||||
if (method != null){
|
||||
method.releaseConnection();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return user.getPlan().isPrivateRepoAllowed();
|
||||
}
|
||||
|
||||
public static boolean checkCredentials(final Project project) {
|
||||
@@ -291,6 +420,7 @@ public class GithubUtil {
|
||||
// Otherwise our credentials are valid and they are successfully stored in settings
|
||||
final String validPassword = settings.getPassword();
|
||||
return accessToGithubWithModalProgress(project, new Computable<RepositoryInfo>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public RepositoryInfo compute() {
|
||||
ProgressManager.getInstance().getProgressIndicator().setText("Extracting detailed info about repository ''" + name + "''");
|
||||
@@ -327,7 +457,7 @@ public class GithubUtil {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static boolean testGitExecutable(final Project project) {
|
||||
final GitVcsApplicationSettings settings = GitVcsApplicationSettings.getInstance();
|
||||
final String executable = settings.getPathToGit();
|
||||
|
||||
@@ -1,55 +1,59 @@
|
||||
package org.jetbrains.plugins.github;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author oleg
|
||||
* @date 10/21/10
|
||||
*/
|
||||
* Information about Github repository.
|
||||
*
|
||||
* @author oleg
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class RepositoryInfo {
|
||||
private final Element myRepository;
|
||||
|
||||
public RepositoryInfo(final Element repository) {
|
||||
myRepository = repository;
|
||||
@NotNull private final String myName;
|
||||
@NotNull private final String myCloneUrl;
|
||||
@NotNull private final String myOwnerName;
|
||||
@Nullable private final String myParentName;
|
||||
private final boolean myFork;
|
||||
|
||||
public RepositoryInfo(@NotNull String name, @NotNull String cloneUrl, @NotNull String ownerName, @Nullable String parentName,
|
||||
boolean fork) {
|
||||
myName = name;
|
||||
myParentName = parentName;
|
||||
myCloneUrl = cloneUrl;
|
||||
myOwnerName = ownerName;
|
||||
myFork = fork;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myRepository.getChildText("name");
|
||||
return myName;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return myRepository.getChildText("owner");
|
||||
@NotNull
|
||||
public String getOwnerName() {
|
||||
return myOwnerName;
|
||||
}
|
||||
|
||||
public boolean isFork() {
|
||||
return Boolean.valueOf(myRepository.getChildText("fork"));
|
||||
return myFork;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
return myRepository.getChildText("parent");
|
||||
/**
|
||||
* @return The name of the parent of this repository, or null.
|
||||
* Null is returned if this repository doesn't have a parent, i. e. is not a fork,
|
||||
* or if the parent information was not retrieved by the time of constructing of this RepositoryInfo object.
|
||||
* To be sure use {@link #isFork()}.
|
||||
*/
|
||||
@Nullable
|
||||
public String getParentName() {
|
||||
return myParentName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return getOwner() + "/" + getName();
|
||||
@NotNull
|
||||
public String getCloneUrl() {
|
||||
return myCloneUrl;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return myRepository.getChildText("url") + ".git";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myRepository != null ? myRepository.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof RepositoryInfo)){
|
||||
return false;
|
||||
}
|
||||
final RepositoryInfo repositoryInfo = (RepositoryInfo)obj;
|
||||
return Comparing.equal(getName(), repositoryInfo.getName()) &&
|
||||
Comparing.equal(getOwner(), repositoryInfo.getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user