blob: bd8c2f146ca199364fdade687d7a4214e6327037 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 com.google.gct.idea.appengine.deploy;
import com.google.common.base.Strings;
import com.google.gct.idea.appengine.dom.AppEngineWebApp;
import com.google.gct.idea.appengine.dom.AppEngineWebFileDescription;
import com.google.gct.idea.appengine.gradle.facet.AppEngineConfigurationProperties;
import com.google.gct.idea.appengine.gradle.facet.AppEngineGradleFacet;
import com.google.gct.login.GoogleLogin;
import com.google.gct.login.IGoogleLoginCompletedCallback;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.configuration.ModulesCombobox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.artifacts.ArtifactManager;
import com.intellij.packaging.elements.PackagingElementResolvingContext;
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.xml.XmlFile;
import com.intellij.ui.SortedComboBoxModel;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import com.intellij.xml.util.XmlStringUtil;
import org.eclipse.jgit.util.StringUtils;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* AppEngineUpdateDialog shows a dialog allowing the user to select a module and deploy.
*/
public class AppEngineUpdateDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance(AppEngineUpdateDialog.class);
private ModulesCombobox myModuleComboBox;
private JTextField myProjectId;
private JTextField myVersion;
private JPanel myPanel;
private List<Module> myDeployableModules;
private Project myProject;
private Module myInitiallySelectedModule;
private AppEngineUpdateDialog(Project project, List<Module> deployableModules, Module selectedModule) {
super(project, true);
myDeployableModules = deployableModules;
myProject = project;
myInitiallySelectedModule = selectedModule;
init();
initValidation();
setTitle("Deploy to App Engine");
setOKButtonText("Deploy");
Window myWindow = getWindow();
if (myWindow != null) {
myWindow.setPreferredSize(new Dimension(285, 135));
}
}
/**
* Shows a dialog to deploy a module to AppEngine. Will force a login if required
* If either the login fails or there are no valid modules to upload, it will return after
* displaying an error.
*
* @param project The project whose modules will be uploaded.
* @param selectedModule The module selected by default in the deploy dialog. Can be null. If null or not a valid app engine module,
* no module will be selected by default.
*/
static void show(final Project project, Module selectedModule) {
final java.util.List<Module> modules = new ArrayList<Module>();
// Filter the module list by whether we can actually deploy them to appengine.
for (Module module : ModuleManager.getInstance(project).getModules()) {
AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module);
if (facet != null) {
modules.add(module);
}
}
// Tell the user what he has to do if he has none.
if (modules.size() == 0) {
//there are no modules to upload -- or we hit a bug due to gradle sync.
//TODO do we need to use the mainwindow as owner?
Messages.showErrorDialog(
XmlStringUtil.wrapInHtml(
"This project does not contain any App Engine modules. To add an App Engine module for your project, <br> open “File > New Module…” menu and choose one of App Engine modules.")
, "Error");
return;
}
if (selectedModule != null && !modules.contains(selectedModule)) {
selectedModule = null;
}
if (selectedModule == null && modules.size() == 1) {
selectedModule = modules.get(0);
}
// To invoke later, we need a final local.
final Module passedSelectedModule = selectedModule;
// Login on demand and queue up the dialog to show after a successful login.
//if login fails, it already shows an error.
if (!GoogleLogin.getInstance().isLoggedIn()) {
// log in on demand...
GoogleLogin.getInstance().logIn(null, new IGoogleLoginCompletedCallback() {
@Override
public void onLoginCompleted() {
if (GoogleLogin.getInstance().isLoggedIn()) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// Success!, lets run the deploy now.
AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule);
dialog.show();
}
});
}
}
});
}
else {
AppEngineUpdateDialog dialog = new AppEngineUpdateDialog(project, modules, passedSelectedModule);
dialog.show();
}
}
@Nullable
@Override
protected JComponent createCenterPanel() {
@SuppressWarnings("unchecked")
final SortedComboBoxModel<Module> model = (SortedComboBoxModel<Module>)myModuleComboBox.getModel();
model.clear();
model.addAll(myDeployableModules);
if (myInitiallySelectedModule != null) {
// Auto select if there is only one item
model.setSelectedItem(myInitiallySelectedModule);
populateFields();
}
myModuleComboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
populateFields();
}
});
return myPanel;
}
private void populateFields() {
myProjectId.setText("");
myVersion.setText("");
Module appEngineModule = myModuleComboBox.getSelectedModule();
if (appEngineModule != null) {
AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(appEngineModule);
if (facet == null) {
Messages.showErrorDialog(this.getPeer().getOwner(), "Could not acquire App Engine module information.", "Deploy");
return;
}
final AppEngineWebApp appEngineWebApp = facet.getAppEngineWebXml();
if (appEngineWebApp == null) {
Messages.showErrorDialog(this.getPeer().getOwner(), "Could not locate or parse the appengine-web.xml fle.", "Deploy");
return;
}
myProjectId.setText(appEngineWebApp.getApplication().getRawText());
myVersion.setText(appEngineWebApp.getVersion().getRawText());
}
}
@Override
protected void doOKAction() {
if (getOKAction().isEnabled()) {
GoogleLogin login = GoogleLogin.getInstance();
Module selectedModule = myModuleComboBox.getSelectedModule();
String sdk = "";
String war = "";
AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(selectedModule);
if (facet != null) {
AppEngineConfigurationProperties model = facet.getConfiguration().getState();
sdk = model.APPENGINE_SDKROOT;
war = model.WAR_DIR;
}
String client_secret = login.fetchOAuth2ClientSecret();
String client_id = login.fetchOAuth2ClientId();
String refresh_token = login.fetchOAuth2RefreshToken();
if (StringUtils.isEmptyOrNull(client_secret) ||
StringUtils.isEmptyOrNull(client_id) ||
StringUtils.isEmptyOrNull(refresh_token)) {
// The login is somehow invalid, bail -- this shouldn't happen.
LOG.error("StartUploading while logged in, but it doesn't have full credentials.");
Messages.showErrorDialog(this.getPeer().getOwner(), "Login credentials are not valid.", "Login");
return;
}
// These should not fail as they are a part of the dialog validation.
if (Strings.isNullOrEmpty(sdk) ||
Strings.isNullOrEmpty(war) ||
Strings.isNullOrEmpty(myProjectId.getText()) ||
selectedModule == null) {
Messages.showErrorDialog(this.getPeer().getOwner(), "Could not deploy due to missing information (sdk/war/projectid).", "Deploy");
LOG.error("StartUploading was called with bad module/sdk/war");
return;
}
close(OK_EXIT_CODE); // We close before kicking off the update so it doesn't interfere with the output window coming to focus.
// Kick off the upload. detailed status will be shown in an output window.
new AppEngineUpdater(myProject, selectedModule, sdk, war, myProjectId.getText(), myVersion.getText(),
client_secret, client_id, refresh_token).startUploading();
}
}
@Override
protected ValidationInfo doValidate() {
// These should not normally occur..
if (!GoogleLogin.getInstance().isLoggedIn()) {
return new ValidationInfo("You must be logged in to perform this action.");
}
Module module = myModuleComboBox.getSelectedModule();
if (module == null) {
return new ValidationInfo("Select a module");
}
AppEngineGradleFacet facet = AppEngineGradleFacet.getAppEngineFacetByModule(module);
if (facet == null) {
return new ValidationInfo("Could not find App Engine gradle configuration on Module");
}
// We'll let AppCfg error if the project is wrong. The user can see this in the console window.
// Note that version can be blank to indicate current version.
if (Strings.isNullOrEmpty(myProjectId.getText())) {
return new ValidationInfo("Please enter a Project ID.");
}
return null;
}
}