/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.projectimport.wizard;
import com.google.common.base.Strings;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.core.model.project.SourceStorage;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.RequestCall;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.importer.AbstractImporter;
import org.eclipse.che.ide.api.oauth.OAuth2Authenticator;
import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorRegistry;
import org.eclipse.che.ide.api.oauth.OAuth2AuthenticatorUrlProvider;
import org.eclipse.che.ide.api.project.MutableProjectConfig;
import org.eclipse.che.ide.api.project.wizard.ImportProjectNotificationSubscriberFactory;
import org.eclipse.che.ide.api.project.wizard.ProjectNotificationSubscriber;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.user.Credentials;
import org.eclipse.che.ide.api.user.AskCredentialsDialog;
import org.eclipse.che.ide.api.wizard.Wizard.CompleteCallback;
import org.eclipse.che.ide.resource.Path;
import org.eclipse.che.ide.util.ExceptionUtils;
import org.eclipse.che.security.oauth.OAuthStatus;
import java.util.Map;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.api.core.ErrorCodes.UNABLE_GET_PRIVATE_SSH_KEY;
import static org.eclipse.che.api.core.ErrorCodes.UNAUTHORIZED_GIT_OPERATION;
import static org.eclipse.che.api.core.ErrorCodes.UNAUTHORIZED_SVN_OPERATION;
import static org.eclipse.che.api.git.shared.ProviderInfo.AUTHENTICATE_URL;
import static org.eclipse.che.api.git.shared.ProviderInfo.PROVIDER_NAME;
import static org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.createFromAsyncRequest;
import static org.eclipse.che.ide.util.ExceptionUtils.getErrorCode;
/**
* @author Dmitry Shnurenko
* @author Vlad Zhukovskyi
*/
@Singleton
public class ProjectImporter extends AbstractImporter {
private final CoreLocalizationConstant localizationConstant;
private final ProjectResolver projectResolver;
private final AskCredentialsDialog credentialsDialog;
private final OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry;
@Inject
public ProjectImporter(CoreLocalizationConstant localizationConstant,
ImportProjectNotificationSubscriberFactory subscriberFactory,
AppContext appContext,
ProjectResolver projectResolver,
AskCredentialsDialog credentialsDialog,
OAuth2AuthenticatorRegistry oAuth2AuthenticatorRegistry) {
super(appContext, subscriberFactory);
this.localizationConstant = localizationConstant;
this.projectResolver = projectResolver;
this.credentialsDialog = credentialsDialog;
this.oAuth2AuthenticatorRegistry = oAuth2AuthenticatorRegistry;
}
public void importProject(final CompleteCallback callback, MutableProjectConfig projectConfig) {
final Path path = !isNullOrEmpty(projectConfig.getPath())
? Path.valueOf(projectConfig.getPath())
: !isNullOrEmpty(projectConfig.getName()) ? Path.valueOf(projectConfig.getName()).makeAbsolute()
: null;
checkState(path != null, "Import path is undefined");
startImport(path, projectConfig.getSource()).then(new Operation<Project>() {
@Override
public void apply(Project arg) throws OperationException {
if (callback != null) {
callback.onCompleted();
}
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
if (callback != null) {
callback.onFailure(arg.getCause());
}
}
});
}
@Override
protected Promise<Project> importProject(Path path, SourceStorage sourceStorage) {
return doImport(path, sourceStorage);
}
private Promise<Project> doImport(final Path path, final SourceStorage sourceStorage) {
final ProjectNotificationSubscriber subscriber = subscriberFactory.createSubscriber();
subscriber.subscribe(path.lastSegment());
MutableProjectConfig importConfig = new MutableProjectConfig();
importConfig.setPath(path.toString());
importConfig.setSource(sourceStorage);
return appContext.getWorkspaceRoot()
.importProject()
.withBody(importConfig)
.send()
.thenPromise(new Function<Project, Promise<Project>>() {
@Override
public Promise<Project> apply(Project project) throws FunctionException {
subscriber.onSuccess();
return projectResolver.resolve(project);
}
})
.catchErrorPromise(new Function<PromiseError, Promise<Project>>() {
@Override
public Promise<Project> apply(PromiseError exception) throws FunctionException {
subscriber.onFailure(exception.getCause().getMessage());
switch (getErrorCode(exception.getCause())) {
case UNABLE_GET_PRIVATE_SSH_KEY:
throw new IllegalStateException(localizationConstant.importProjectMessageUnableGetSshKey());
case UNAUTHORIZED_SVN_OPERATION:
return recallImportWithCredentials(sourceStorage, path);
case UNAUTHORIZED_GIT_OPERATION:
final Map<String, String> attributes = ExceptionUtils.getAttributes(exception.getCause());
final String providerName = attributes.get(PROVIDER_NAME);
final String authenticateUrl = attributes.get(AUTHENTICATE_URL);
if (!Strings.isNullOrEmpty(providerName) && !Strings.isNullOrEmpty(authenticateUrl)) {
return authUserAndRecallImport(providerName,
authenticateUrl,
path,
sourceStorage,
subscriber);
} else {
throw new IllegalStateException(localizationConstant.oauthFailedToGetAuthenticatorText());
}
default:
throw new IllegalStateException(exception.getCause());
}
}
});
}
private Promise<Project> recallImportWithCredentials(final SourceStorage sourceStorage, final Path path) {
return createFromAsyncRequest(new RequestCall<Project>() {
@Override
public void makeCall(final AsyncCallback<Project> callback) {
credentialsDialog.askCredentials().then(new Operation<Credentials>() {
@Override
public void apply(Credentials credentials) throws OperationException {
sourceStorage.getParameters().put("username", credentials.getUsername());
sourceStorage.getParameters().put("password", credentials.getPassword());
doImport(path, sourceStorage).then(new Operation<Project>() {
@Override
public void apply(Project project) throws OperationException {
callback.onSuccess(project);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
callback.onFailure(error.getCause());
}
});
}
});
}
});
}
private Promise<Project> authUserAndRecallImport(final String providerName,
final String authenticateUrl,
final Path path,
final SourceStorage sourceStorage,
final ProjectNotificationSubscriber subscriber) {
return createFromAsyncRequest(new RequestCall<Project>() {
@Override
public void makeCall(final AsyncCallback<Project> callback) {
OAuth2Authenticator authenticator = oAuth2AuthenticatorRegistry.getAuthenticator(providerName);
if (authenticator == null) {
authenticator = oAuth2AuthenticatorRegistry.getAuthenticator("default");
}
authenticator.authenticate(OAuth2AuthenticatorUrlProvider.get(appContext.getMasterEndpoint(), authenticateUrl),
new AsyncCallback<OAuthStatus>() {
@Override
public void onFailure(Throwable caught) {
callback.onFailure(new Exception(caught.getMessage()));
}
@Override
public void onSuccess(OAuthStatus result) {
if (!result.equals(OAuthStatus.NOT_PERFORMED)) {
doImport(path, sourceStorage).then(new Operation<Project>() {
@Override
public void apply(Project project) throws OperationException {
callback.onSuccess(project);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError error) throws OperationException {
callback.onFailure(error.getCause());
}
});
} else {
subscriber.onFailure("Authentication cancelled");
callback.onFailure(new IllegalStateException("Authentication cancelled"));
}
}
});
}
});
}
}