diff options
Diffstat (limited to 'src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java')
-rw-r--r-- | src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java new file mode 100644 index 0000000..bd8c2f1 --- /dev/null +++ b/src/com/google/gct/idea/appengine/deploy/AppEngineUpdateDialog.java @@ -0,0 +1,275 @@ +/* + * 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; + } + +} |