diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/OCINode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/OCINode.java index 4d25183bac78..1e058c7170a2 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/OCINode.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/OCINode.java @@ -19,11 +19,11 @@ package org.netbeans.modules.cloud.oracle; import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import javax.swing.Action; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import org.netbeans.modules.cloud.oracle.items.OCIItem; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -121,10 +121,14 @@ public Node.Handle getHandle() { return super.getHandle(); } - private final class RefreshListener implements ChangeListener { + private final class RefreshListener implements PropertyChangeListener { @Override - public void stateChanged(ChangeEvent e) { - refresh(); + public void propertyChange(PropertyChangeEvent evt) { + if ("referenceName".equals(evt.getPropertyName())) { + fireDisplayNameChange("", getDisplayName()); + } else { + refresh(); + } } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddADBAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddADBAction.java index d20706a03454..224db73674b0 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddADBAction.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddADBAction.java @@ -75,14 +75,16 @@ @NbBundle.Messages({ "AddADB=Add Oracle Autonomous DB", "SelectProfile=Select OCI Profile", - "# {0} - tenancy name", - "# {1} - region id", - "SelectProfile_Description={0} (region: {1})", "SelectCompartment=Select Compartment", "SelectDatabase=Select Database", "NoDatabase=No Database available in this Compartment", "EnterUsername=Enter Username", - "EnterPassword=Enter Password" + "EnterPassword=Enter Password", + "MSG_CollectingProfiles=Searching for OCI Profiles", + "MSG_CollectingProfiles_Text=Loading OCI Profiles", + "MSG_CollectingItems=Loading OCI contents", + "MSG_CollectingItems_Text=Listing compartments and databases", + "SelectProfile_Description={0} (region: {1})" }) public class AddADBAction implements ActionListener { private static final Logger LOGGER = Logger.getLogger(AddADBAction.class.getName()); @@ -92,12 +94,6 @@ public class AddADBAction implements ActionListener { private static final String PASSWORD = "password"; //NOI18N private static final int NUMBER_OF_INPUTS = 4; - @NbBundle.Messages({ - "MSG_CollectingProfiles=Searching for OCI Profiles", - "MSG_CollectingProfiles_Text=Loading OCI Profiles", - "MSG_CollectingItems=Loading OCI contents", - "MSG_CollectingItems_Text=Listing compartments and databases", - }) @Override public void actionPerformed(ActionEvent e) { addADB(); @@ -212,8 +208,7 @@ public NotifyDescriptor createInput(NotifyDescriptor.ComposedInput input, int nu AbstractPasswordPanel.generatePassword(), (String) result.get(USERNAME), ((String) result.get(PASSWORD)).toCharArray(), - selectedDatabase.getKey().getValue(), - selectedDatabase.getCompartmentId()); + selectedDatabase); action.addConnection(info); return selectedDatabase; } catch (IOException ex) { diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java index e4d0dc0eb6ae..1e43599166ec 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVault.java @@ -18,19 +18,19 @@ */ package org.netbeans.modules.cloud.oracle.actions; +import org.netbeans.modules.cloud.oracle.steps.PasswordStep; +import org.netbeans.modules.cloud.oracle.steps.DatasourceNameStep; +import org.netbeans.modules.cloud.oracle.steps.OverwriteStep; import com.oracle.bmc.devops.DevopsClient; import com.oracle.bmc.devops.model.DeployArtifactSource; import com.oracle.bmc.devops.model.DeployArtifactSummary; import com.oracle.bmc.devops.model.InlineDeployArtifactSource; -import com.oracle.bmc.devops.model.ProjectSummary; import com.oracle.bmc.devops.model.UpdateDeployArtifactDetails; import com.oracle.bmc.devops.requests.GetDeployArtifactRequest; import com.oracle.bmc.devops.requests.ListDeployArtifactsRequest; -import com.oracle.bmc.devops.requests.ListProjectsRequest; import com.oracle.bmc.devops.requests.UpdateDeployArtifactRequest; import com.oracle.bmc.devops.responses.GetDeployArtifactResponse; import com.oracle.bmc.devops.responses.ListDeployArtifactsResponse; -import com.oracle.bmc.devops.responses.ListProjectsResponse; import org.netbeans.api.db.explorer.DatabaseConnection; import com.oracle.bmc.model.BmcException; import com.oracle.bmc.vault.VaultsClient; @@ -51,20 +51,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; import java.util.UUID; -import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.project.Project; @@ -72,31 +66,24 @@ import static org.netbeans.modules.cloud.oracle.OCIManager.getDefault; import org.netbeans.modules.cloud.oracle.assets.DependencyUtils; import org.netbeans.modules.cloud.oracle.assets.Steps; -import org.netbeans.modules.cloud.oracle.assets.Step; +import org.netbeans.modules.cloud.oracle.steps.CompartmentStep; +import org.netbeans.modules.cloud.oracle.steps.DevopsStep; import org.netbeans.modules.cloud.oracle.assets.Steps.NextStepProvider; -import org.netbeans.modules.cloud.oracle.assets.Steps.ProjectStep; -import org.netbeans.modules.cloud.oracle.assets.Steps.TenancyStep; -import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; +import org.netbeans.modules.cloud.oracle.steps.KeyStep; +import org.netbeans.modules.cloud.oracle.steps.ProjectStep; +import org.netbeans.modules.cloud.oracle.steps.TenancyStep; +import org.netbeans.modules.cloud.oracle.steps.VaultStep; import org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem; -import org.netbeans.modules.cloud.oracle.devops.DevopsProjectService; -import org.netbeans.modules.cloud.oracle.items.OCID; -import org.netbeans.modules.cloud.oracle.items.OCIItem; import org.netbeans.modules.cloud.oracle.vault.KeyItem; -import org.netbeans.modules.cloud.oracle.vault.KeyNode; -import org.netbeans.modules.cloud.oracle.vault.SecretItem; -import org.netbeans.modules.cloud.oracle.vault.SecretNode; import org.netbeans.modules.cloud.oracle.vault.VaultItem; -import org.netbeans.modules.cloud.oracle.vault.VaultNode; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; -import org.openide.NotifyDescriptor.QuickPick.Item; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.util.Lookup; import org.openide.util.NbBundle; -import org.openide.util.Pair; import org.openide.util.lookup.Lookups; /** @@ -116,20 +103,8 @@ }) @NbBundle.Messages({ "AddADBToVault=Add Oracle Autonomous DB details to OCI Vault", - "SelectKey=Select Key", - "SelectVault=Select Vault", "SecretsCreated=Secrets were created or updated", - "NoKeys=No keys in this Vault. Select another one.", - "DatasourceName=Datasource Name", - "AddVersion=Add new versions", - "Cancel=Cancel", - "SecretExists=Secrets with name {0} already exists", - "NoProfile=There is not any OCI profile in the config", - "NoCompartment=There are no compartments in the Tenancy", - "Password=Enter password for Database user {0}", "NoConfigMap=No ConfigMap found in the Devops project {0}", - "SelectDevopsProject=Select Devops Project", - "NoDevopsProjects=There are no Devops Projects in selected Compartment", "ConfigmapUpdateFailed=Failed to update ConfigMap", "CreatingSecret=Creating secret {0}", "UpdatingSecret=Updating secret {0}", @@ -138,7 +113,6 @@ "DatasourceEmpty=Datasource name cannot be empty" }) public class AddDbConnectionToVault implements ActionListener { - private static final Logger LOG = Logger.getLogger(AddDbConnectionToVault.class.getName()); private final DatabaseConnection context; @@ -146,357 +120,37 @@ public class AddDbConnectionToVault implements ActionListener { public AddDbConnectionToVault(DatabaseConnection context) { this.context = context; } - - class VaultStep implements Step { - - private Map vaults = null; - private VaultItem selected; - private Lookup lookup; - - public Step prepare(CompartmentItem compartment, Lookup lookup) { - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.MSG_CollectingItems()); - h.start(); - h.progress(Bundle.MSG_CollectingItems_Text()); - try { - vaults = getVaults(compartment); - } finally { - h.finish(); - } - return this; - } - - @Override - public NotifyDescriptor createInput() { - return createQuickPick(vaults, Bundle.SelectVault()); - } - - @Override - public Step getNext() { - return new KeyStep().prepare(getValue(), lookup); - } - - @Override - public void setValue(String selected) { - this.selected = vaults.get(selected); - } - - @Override - public VaultItem getValue() { - if (onlyOneChoice()) { - selected = vaults.values().iterator().next(); - } - return selected; - } - - @Override - public boolean onlyOneChoice() { - return vaults.size() == 1; - } - } - - class KeyStep implements Step> { - - private Map keys = null; - private KeyItem selected; - private VaultItem vault; - private Lookup lookup; - - public Step> prepare(VaultItem vault, Lookup lookup) { - this.vault = vault; - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.MSG_CollectingItems()); - h.start(); - h.progress(Bundle.MSG_CollectingItems_Text()); - try { - keys = getKeys(vault); - } finally { - h.finish(); - } - return this; - } - - @Override - public boolean onlyOneChoice() { - return keys.size() == 1; - } - - @Override - public NotifyDescriptor createInput() { - if (keys.size() > 1) { - return createQuickPick(keys, Bundle.SelectKey()); - } - if (keys.size() == 0) { - return new NotifyDescriptor.QuickPick("", Bundle.NoKeys(), Collections.emptyList(), false); - } - - throw new IllegalStateException("No data to create input"); // NOI18N - } - - @Override - public Step getNext() { - return new DatasourceNameStep().prepare(getValue(), lookup); - } - - @Override - public void setValue(String selected) { - this.selected = keys.get(selected); - } - - @Override - public Pair getValue() { - if (keys.size() == 1) { - return Pair.of(vault, keys.values().iterator().next()); - } - return Pair.of(vault, selected); - } - - } - - class DatasourceNameStep implements Step, Result> { - - private Result result = new Result(); - private Lookup lookup; - - @Override - public Step, Result> prepare(Pair item, Lookup lookup) { - this.lookup = lookup; - result.vault = item.first(); - result.key = item.second(); - return this; - } - - @Override - public NotifyDescriptor createInput() { - return new NotifyDescriptor.InputLine("DEFAULT", Bundle.DatasourceName()); //NOI18N - } - - @Override - public Step getNext() { - return new OverwriteStep().prepare(result, lookup); - } - - @Override - public void setValue(String selected) { - result.datasourceName = selected; - } - - @Override - public Result getValue() { - return result; - } - - @Override - public boolean onlyOneChoice() { - return false; - } - - } - - class OverwriteStep implements Step { - - private Result result; - private Set dsNames; - private String choice; - private Lookup lookup; - - @Override - public Step prepare(Result result, Lookup lookup) { - this.lookup = lookup; - this.result = result; - if (result.datasourceName == null || result.datasourceName.isEmpty()) { - return this; - } - List secrets = SecretNode.getSecrets().apply(result.vault); - this.dsNames = secrets.stream() - .map(s -> extractDatasourceName(s.getName())) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - return this; - } - - @Override - public NotifyDescriptor createInput() { - if (result.datasourceName == null || result.datasourceName.isEmpty()) { - return new NotifyDescriptor.QuickPick("", Bundle.DatasourceEmpty(), Collections.emptyList(), false); - } - List yesNo = new ArrayList(); - yesNo.add(new Item(Bundle.AddVersion(), "")); - yesNo.add(new Item(Bundle.Cancel(), "")); - return new NotifyDescriptor.QuickPick("", Bundle.SecretExists(result.datasourceName), yesNo, false); - } - - @Override - public Step getNext() { - return new PasswordStep().prepare(result, lookup); - } - - @Override - public void setValue(String choice) { - this.choice = choice; - } - - @Override - public Result getValue() { - if (Bundle.AddVersion().equals(choice) || onlyOneChoice()) { - result.update = true; - return result; - } - return null; - } - - @Override - public boolean onlyOneChoice() { - return dsNames != null && !dsNames.contains(result.datasourceName); - } - - } - - class PasswordStep implements Step { - private Result item; - private boolean ask; - private Lookup lookup; - - @Override - public Step prepare(Result item, Lookup lookup) { - this.lookup = lookup; - item.password = context.getPassword(); - ask = item.password == null || item.password.isEmpty(); - this.item = item; - return this; - } - - @Override - public NotifyDescriptor createInput() { - return new NotifyDescriptor.PasswordLine("DEFAULT", Bundle.Password(context.getUser())); //NOI18N - } - - @Override - public boolean onlyOneChoice() { - return !ask; - } - - @Override - public Step getNext() { - return new DevopsStep().prepare(item, lookup); - } - - @Override - public void setValue(String password) { - item.password = password; - } - - @Override - public Result getValue() { - return item; - } - } - - class DevopsStep implements Step { - private Result item; - private Map devopsProjects; - private Lookup lookup; - - @Override - public Step prepare(Result item, Lookup lookup) { - this.item = item; - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.MSG_CollectingItems()); - h.start(); - h.progress(Bundle.MSG_CollectingItems_Text()); - try { - List devops = DevopsProjectService.getDevopsProjectOcid(); - - Map allProjectsInCompartment = getDevopsProjects(item.vault.getCompartmentId()); - Map filtered = allProjectsInCompartment.entrySet() - .stream() - .filter(e -> devops.contains(e.getValue().getKey().getValue())) - .collect(Collectors - .toMap(Entry::getKey, Entry::getValue)); - if (filtered.size() > 0) { - devopsProjects = filtered; - } else { - devopsProjects = allProjectsInCompartment; - } - if (devopsProjects.size() == 1) { - item.project = devopsProjects.values().iterator().next(); - } - - } finally { - h.finish(); - } - return this; - } - - @Override - public NotifyDescriptor createInput() { - if (devopsProjects.size() > 1) { - return createQuickPick(devopsProjects, Bundle.SelectDevopsProject()); - } - if (devopsProjects.isEmpty()) { - return new NotifyDescriptor.QuickPick("", Bundle.NoDevopsProjects(), Collections.emptyList(), false); - } - throw new IllegalStateException("No data to create input"); // NOI18N - } - - @Override - public boolean onlyOneChoice() { - return devopsProjects.size() == 1; - } - - @Override - public Step getNext() { - NextStepProvider nsProvider = lookup.lookup(NextStepProvider.class); - if (nsProvider != null) { - Step ns = nsProvider.nextStepFor(this); - if (ns != null) { - return ns.prepare(getValue(), lookup); - } - } - return null; - } - - @Override - public void setValue(String projectName) { - item.project = devopsProjects.get(projectName); - } - - @Override - public Result getValue() { - return item; - } - } - - static final class Result { - VaultItem vault; - KeyItem key; - String datasourceName; - String password; - DevopsProjectItem project; - private boolean update; - } @Override public void actionPerformed(ActionEvent e) { NextStepProvider nsProvider = NextStepProvider.builder() - .stepForClass(Steps.CompartmentStep.class, (s) -> new VaultStep()) + .stepForClass(CompartmentStep.class, (s) -> new VaultStep()) + .stepForClass(VaultStep.class, (s) -> new KeyStep()) + .stepForClass(KeyStep.class, (s) -> new DatasourceNameStep()) + .stepForClass(DatasourceNameStep.class, (s) -> new OverwriteStep()) + .stepForClass(OverwriteStep.class, (s) -> new PasswordStep(context.getPassword(), context.getUser())) + .stepForClass(PasswordStep.class, (s) -> new DevopsStep()) .stepForClass(DevopsStep.class, (s) -> new ProjectStep()) .build(); Lookup lookup = Lookups.fixed(nsProvider); - Steps.getDefault().executeMultistep(new TenancyStep(), lookup).thenAccept(r -> { - Result result = ((Pair) r).second(); - Project project = ((Pair) r).first(); - if (result.datasourceName == null || result.datasourceName.isEmpty()) { + Steps.getDefault().executeMultistep(new TenancyStep(), lookup).thenAccept(vals -> { + Project project = vals.getValueForStep(ProjectStep.class); + DevopsProjectItem devopsProject = vals.getValueForStep(DevopsStep.class); + String datasourceName = vals.getValueForStep(DatasourceNameStep.class); + VaultItem vault = vals.getValueForStep(VaultStep.class); + KeyItem key = vals.getValueForStep(KeyStep.class); + String password = vals.getValueForStep(PasswordStep.class); + if (datasourceName == null || datasourceName.isEmpty()) { NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.DatasourceEmpty()); DialogDisplayer.getDefault().notify(msg); return; } - addDbConnectionToVault(result, project); + addDbConnectionToVault(vault, key, project, devopsProject, datasourceName, password); }); } - private void addDbConnectionToVault(Result item, Project project) { - ProgressHandle h = ProgressHandle.createHandle(Bundle.UpdatingVault(item.vault.getName())); + private void addDbConnectionToVault(VaultItem vault, KeyItem key, Project project, DevopsProjectItem devopsProject, String datasourceName, String password) { + ProgressHandle h = ProgressHandle.createHandle(Bundle.UpdatingVault(vault.getName())); h.start(); h.progress(Bundle.ReadingSecrets()); @@ -504,20 +158,20 @@ private void addDbConnectionToVault(Result item, Project project) { VaultsClient client = VaultsClient.builder().build(getDefault().getActiveProfile().getConfigProvider()); ListSecretsRequest listSecretsRequest = ListSecretsRequest.builder() - .compartmentId(item.vault.getCompartmentId()) - .vaultId(item.vault.getKey().getValue()) + .compartmentId(vault.getCompartmentId()) + .vaultId(vault.getKey().getValue()) .limit(88) .build(); ListSecretsResponse secrets = client.listSecrets(listSecretsRequest); - Map existingSecrets = secrets.getItems().stream() - .collect(Collectors.toMap(s -> s.getSecretName(), s -> s.getId())); + Map existingSecrets = secrets.getItems().stream() + .collect(Collectors.toMap(s -> s.getSecretName(), s -> s.getId())); Map values = new HashMap() { { put("Username", context.getUser()); //NOI18N - put("Password", item.password); //NOI18N + put("Password", password); //NOI18N put("OCID", (String) context.getConnectionProperties().get("OCID")); //NOI18N put("CompartmentOCID", (String) context.getConnectionProperties().get("CompartmentOCID")); //NOI18N put("wallet_Password", UUID.randomUUID().toString()); //NOI18N @@ -525,7 +179,7 @@ private void addDbConnectionToVault(Result item, Project project) { }; for (Entry entry : values.entrySet()) { - String secretName = "DATASOURCES_" + item.datasourceName + "_" + entry.getKey().toUpperCase(); //NOI18N + String secretName = "DATASOURCES_" + datasourceName + "_" + entry.getKey().toUpperCase(); //NOI18N String base64Content = Base64.getEncoder().encodeToString(entry.getValue().getBytes(StandardCharsets.UTF_8)); SecretContentDetails contentDetails = Base64SecretContentDetails.builder() @@ -553,9 +207,9 @@ private void addDbConnectionToVault(Result item, Project project) { .secretContent(contentDetails) .secretRules(new ArrayList<>(Arrays.asList(SecretReuseRule.builder() .isEnforcedOnDeletedSecretVersions(false).build()))) - .compartmentId(item.vault.getCompartmentId()) - .vaultId(item.vault.getKey().getValue()) - .keyId(item.key.getKey().getValue()) + .compartmentId(vault.getCompartmentId()) + .vaultId(vault.getKey().getValue()) + .keyId(key.getKey().getValue()) .build(); CreateSecretRequest request = CreateSecretRequest .builder() @@ -575,13 +229,13 @@ private void addDbConnectionToVault(Result item, Project project) { // Add Vault to the ConfigMap artifact DevopsClient devopsClient = DevopsClient.builder().build(OCIManager.getDefault().getActiveProfile().getConfigProvider()); ListDeployArtifactsRequest request = ListDeployArtifactsRequest.builder() - .projectId(item.project.getKey().getValue()).build(); + .projectId(devopsProject.getKey().getValue()).build(); ListDeployArtifactsResponse response = devopsClient.listDeployArtifacts(request); List artifacts = response.getDeployArtifactCollection().getItems(); boolean found = false; for (DeployArtifactSummary artifact : artifacts) { - if ((item.project.getName() + "_oke_configmap").equals(artifact.getDisplayName())) { //NOI18N - h.progress("updating " + item.project.getName() + "_oke_configmap"); //NOI18N + if ((devopsProject.getName() + "_oke_configmap").equals(artifact.getDisplayName())) { //NOI18N + h.progress("updating " + devopsProject.getName() + "_oke_configmap"); //NOI18N found = true; GetDeployArtifactRequest artRequest = GetDeployArtifactRequest.builder().deployArtifactId(artifact.getId()).build(); GetDeployArtifactResponse artResponse = devopsClient.getDeployArtifact(artRequest); @@ -589,7 +243,7 @@ private void addDbConnectionToVault(Result item, Project project) { if (source instanceof InlineDeployArtifactSource) { byte[] content = ((InlineDeployArtifactSource) source).getBase64EncodedContent(); String srcString = updateProperties(new String(content, StandardCharsets.UTF_8), - item.vault.getCompartmentId(), item.vault.getKey().getValue(), item.datasourceName); + vault.getCompartmentId(), vault.getKey().getValue(), datasourceName); byte[] base64Content = Base64.getEncoder().encode(srcString.getBytes(StandardCharsets.UTF_8)); DeployArtifactSource updatedSource = InlineDeployArtifactSource.builder() .base64EncodedContent(base64Content).build(); @@ -605,7 +259,7 @@ private void addDbConnectionToVault(Result item, Project project) { } } if (!found) { - NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.NoConfigMap(item.project.getName()), NotifyDescriptor.WARNING_MESSAGE); + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.NoConfigMap(devopsProject.getName()), NotifyDescriptor.WARNING_MESSAGE); DialogDisplayer.getDefault().notify(msg); } NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.SecretsCreated()); @@ -644,7 +298,7 @@ protected static String updateProperties(String configmap, String compartmentOci path.entrySet().removeIf(entry -> entry.getKey() >= f); if (propertiesName != null) { int propIndent = previousIndent; - if (properties.size() == 0) { + if (properties.isEmpty()) { propIndent = indent + 2; } output.append( @@ -716,63 +370,4 @@ private static String formatProperties(String proprtiesName, Map return output.toString(); } - private static NotifyDescriptor.QuickPick createQuickPick(Map ociItems, String title) { - - List items = ociItems.entrySet().stream() - .map(entry -> new NotifyDescriptor.QuickPick.Item(entry.getKey(), entry.getValue().getDescription())) - .collect(Collectors.toList()); - return new NotifyDescriptor.QuickPick(title, title, items, false); - } - - protected static Map getDevopsProjects(String compartmentId) { - try (DevopsClient client = new DevopsClient(OCIManager.getDefault().getConfigProvider());) { - ListProjectsRequest request = ListProjectsRequest.builder().compartmentId(compartmentId).build(); - ListProjectsResponse response = client.listProjects(request); - - List projects = response.getProjectCollection().getItems(); - for (ProjectSummary project : projects) { - project.getNotificationConfig().getTopicId(); - - } - return projects.stream() - .map(p -> new DevopsProjectItem(OCID.of(p.getId(), "DevopsProject"), // NOI18N - compartmentId, - p.getName())) - .collect(Collectors.toMap(DevopsProjectItem::getName, Function.identity())); - } - } - - protected static Map getVaults(OCIItem parent) { - Map items = new HashMap<>(); - try { - if (parent instanceof CompartmentItem) { - VaultNode.getVaults().apply((CompartmentItem) parent).forEach((db) -> items.put(db.getName(), db)); - } - } catch (BmcException e) { - LOG.log(Level.SEVERE, "Unable to load vault list", e); //NOI18N - } - return items; - } - - protected static Map getKeys(OCIItem parent) { - Map items = new HashMap<>(); - try { - if (parent instanceof VaultItem) { - KeyNode.getKeys().apply((VaultItem) parent).forEach(key -> items.put(key.getName(), key)); - } - } catch (BmcException e) { - LOG.log(Level.SEVERE, "Unable to load key list", e); //NOI18N - } - return items; - } - - static Pattern p = Pattern.compile("[A-Z]*_([a-zA-Z0-9]*)_[A-Z]*"); //NOI18N - - protected static String extractDatasourceName(String value) { - Matcher m = p.matcher(value); - if (m.matches()) { - return m.group(1); - } - return null; - } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/ConfigMapCommand.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/ConfigMapCommand.java new file mode 100644 index 000000000000..00e8a09f6478 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/ConfigMapCommand.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.actions; + +import com.oracle.bmc.devops.DevopsClient; +import com.oracle.bmc.devops.model.DeployArtifactSource; +import com.oracle.bmc.devops.model.DeployArtifactSummary; +import com.oracle.bmc.devops.model.InlineDeployArtifactSource; +import com.oracle.bmc.devops.model.UpdateDeployArtifactDetails; +import com.oracle.bmc.devops.requests.GetDeployArtifactRequest; +import com.oracle.bmc.devops.requests.ListDeployArtifactsRequest; +import com.oracle.bmc.devops.requests.UpdateDeployArtifactRequest; +import com.oracle.bmc.devops.responses.GetDeployArtifactResponse; +import com.oracle.bmc.devops.responses.ListDeployArtifactsResponse; +import com.oracle.bmc.model.BmcException; +import com.oracle.bmc.vault.VaultsClient; +import com.oracle.bmc.vault.model.Base64SecretContentDetails; +import com.oracle.bmc.vault.model.CreateSecretDetails; +import com.oracle.bmc.vault.model.SecretContentDetails; +import com.oracle.bmc.vault.model.SecretReuseRule; +import com.oracle.bmc.vault.model.UpdateSecretDetails; +import com.oracle.bmc.vault.requests.CreateSecretRequest; +import com.oracle.bmc.vault.requests.ListSecretsRequest; +import com.oracle.bmc.vault.requests.UpdateSecretRequest; +import com.oracle.bmc.vault.responses.ListSecretsResponse; +import com.oracle.bmc.vault.responses.UpdateSecretResponse; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import org.netbeans.modules.cloud.oracle.steps.DevopsStep; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.netbeans.api.db.explorer.ConnectionManager; +import org.netbeans.api.db.explorer.DatabaseConnection; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.project.Project; +import org.netbeans.modules.cloud.oracle.OCIManager; +import static org.netbeans.modules.cloud.oracle.OCIManager.getDefault; +import org.netbeans.modules.cloud.oracle.assets.CloudAssets; +import org.netbeans.modules.cloud.oracle.assets.DependencyUtils; +import org.netbeans.modules.cloud.oracle.assets.PropertiesGenerator; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.database.DatabaseItem; +import org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem; +import org.netbeans.modules.cloud.oracle.steps.DatasourceNameStep; +import org.netbeans.modules.cloud.oracle.steps.KeyStep; +import org.netbeans.modules.cloud.oracle.steps.OverwriteStep; +import org.netbeans.modules.cloud.oracle.steps.PasswordStep; +import org.netbeans.modules.cloud.oracle.steps.ProjectStep; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.steps.CompartmentStep; +import org.netbeans.modules.cloud.oracle.steps.TenancyStep; +import org.netbeans.modules.cloud.oracle.vault.KeyItem; +import org.netbeans.modules.cloud.oracle.vault.VaultItem; +import org.netbeans.spi.lsp.CommandProvider; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; + +/** + * Command that updates a ConfigMap with the current properties generated from the contents of {@link CloudAssets}. + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "SuggestVault=For better security when using Autonomous Database, be sure to also add OCI Vault.", + "UpdatingConfigMap=Updating Config Map", + "CMUpdated=ConfigMap in \"{0}\" project was updated" +}) +@ServiceProvider(service = CommandProvider.class) +public class ConfigMapCommand implements CommandProvider { + + private static final Logger LOG = Logger.getLogger(ConfigMapCommand.class.getName()); + + private static final String COMMAND_UPLOAD_TO_CONFIGMAP = "nbls.cloud.assets.configmap.upload"; //NOI18N + + private static final Set COMMANDS = new HashSet<>(Arrays.asList( + COMMAND_UPLOAD_TO_CONFIGMAP + )); + + @Override + public Set getCommands() { + return Collections.unmodifiableSet(COMMANDS); + } + + @Override + public CompletableFuture runCommand(String command, List arguments) { + Steps.NextStepProvider.Builder nsProviderBuilder = Steps.NextStepProvider.builder(); + CompletableFuture future = new CompletableFuture(); + + nsProviderBuilder.stepForClass(TenancyStep.class, (s) -> new CompartmentStep()) + .stepForClass(CompartmentStep.class, (s) -> new DevopsStep()) + .stepForClass(DevopsStep.class, (s) -> new ProjectStep()); + + Collection items = CloudAssets.getDefault().getAssignedItems(); + AtomicReference vaultRef = new AtomicReference<>(); + AtomicReference dbRef = new AtomicReference<>(); + for (OCIItem item : items) { + if (item instanceof VaultItem) { + vaultRef.set((VaultItem) item); + nsProviderBuilder.stepForClass(OverwriteStep.class, (s) -> new KeyStep(vaultRef.get())); + } else if (item instanceof DatabaseItem) { + dbRef.set((DatabaseItem) item); + DatabaseConnection conn = null; + DatabaseConnection[] connections = ConnectionManager.getDefault().getConnections(); + for (int i = 0; i < connections.length; i++) { + if (item.getKey().getValue().equals( + connections[i].getConnectionProperties().get("OCID"))) { //NOI18N + conn = connections[i]; + break; + } + } + String user, password; + if (conn != null) { + user = conn.getUser(); + password = conn.getPassword(); + } else { + user = null; + password = null; + } + nsProviderBuilder.stepForClass(ProjectStep.class, (s) -> new PasswordStep(user, password)) + .stepForClass(PasswordStep.class, (s) -> new DatasourceNameStep()) + .stepForClass(DatasourceNameStep.class, (s) -> new OverwriteStep()); + } + } + + if (vaultRef.get() == null && dbRef.get() != null) { + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.SuggestVault(), NotifyDescriptor.INFORMATION_MESSAGE); + DialogDisplayer.getDefault().notifyLater(msg); + } + + Lookup lookup = Lookups.fixed(nsProviderBuilder.build()); + Steps.getDefault() + .executeMultistep(new TenancyStep(), lookup) + .thenAccept(values -> { + Project project = values.getValueForStep(ProjectStep.class); + KeyItem key = values.getValueForStep(KeyStep.class); + DevopsProjectItem devopsProject = values.getValueForStep(DevopsStep.class); + String datasourceName = values.getValueForStep(DatasourceNameStep.class); + + if (dbRef.get() != null) { + CloudAssets.getDefault().setReferenceName(dbRef.get(), datasourceName); + } + + ProgressHandle h = ProgressHandle.createHandle(Bundle.UpdatingConfigMap()); + try { + PropertiesGenerator propGen = new PropertiesGenerator(false); + if (vaultRef.get() != null) { + updateVault(h, key, vaultRef.get(), propGen, project); + } + updateConfigMap(h, devopsProject, propGen, command); + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.CMUpdated(devopsProject.getName()), NotifyDescriptor.INFORMATION_MESSAGE); + DialogDisplayer.getDefault().notify(msg); + future.complete(null); + } catch(ThreadDeath e) { + future.completeExceptionally(e); + throw e; + } catch (Throwable e) { + future.completeExceptionally(e); + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(e.getMessage(), NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notify(msg); + } finally { + h.finish(); + } + }); + return future; + } + + private void updateConfigMap(ProgressHandle h, DevopsProjectItem devopsProject, PropertiesGenerator propGen, String datasourceName) { + // Add Vault to the ConfigMap artifact + DevopsClient devopsClient = DevopsClient.builder().build(OCIManager.getDefault().getActiveProfile().getConfigProvider()); + ListDeployArtifactsRequest request = ListDeployArtifactsRequest.builder() + .projectId(devopsProject.getKey().getValue()).build(); + ListDeployArtifactsResponse response = devopsClient.listDeployArtifacts(request); + List artifacts = response.getDeployArtifactCollection().getItems(); + boolean found = false; + for (DeployArtifactSummary artifact : artifacts) { + if ((devopsProject.getName() + "_oke_configmap").equals(artifact.getDisplayName())) { //NOI18N + h.progress("updating " + devopsProject.getName() + "_oke_configmap"); //NOI18N + found = true; + GetDeployArtifactRequest artRequest = GetDeployArtifactRequest.builder().deployArtifactId(artifact.getId()).build(); + GetDeployArtifactResponse artResponse = devopsClient.getDeployArtifact(artRequest); + DeployArtifactSource source = artResponse.getDeployArtifact().getDeployArtifactSource(); + if (source instanceof InlineDeployArtifactSource) { + byte[] content = ((InlineDeployArtifactSource) source).getBase64EncodedContent(); + String srcString = updateProperties(new String(content, StandardCharsets.UTF_8), propGen); + byte[] base64Content = Base64.getEncoder().encode(srcString.getBytes(StandardCharsets.UTF_8)); + DeployArtifactSource updatedSource = InlineDeployArtifactSource.builder() + .base64EncodedContent(base64Content).build(); + UpdateDeployArtifactDetails updateArtifactDetails = UpdateDeployArtifactDetails.builder() + .deployArtifactSource(updatedSource) + .build(); + UpdateDeployArtifactRequest updateArtifactRequest = UpdateDeployArtifactRequest.builder() + .updateDeployArtifactDetails(updateArtifactDetails) + .deployArtifactId(artifact.getId()) + .build(); + devopsClient.updateDeployArtifact(updateArtifactRequest); + } + } + } + if (!found) { + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(org.netbeans.modules.cloud.oracle.actions.Bundle.NoConfigMap(devopsProject.getName()), NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notify(msg); + } + } + + private void updateVault(ProgressHandle h, KeyItem key, VaultItem vault, PropertiesGenerator propGen, Project project) { + h.progress(Bundle.UpdatingVault(vault.getName())); + VaultsClient client = VaultsClient.builder().build(getDefault().getActiveProfile().getConfigProvider()); + ListSecretsRequest listSecretsRequest = ListSecretsRequest.builder() + .compartmentId(vault.getCompartmentId()) + .vaultId(vault.getKey().getValue()) + .limit(88) + .build(); + ListSecretsResponse secrets = client.listSecrets(listSecretsRequest); + Map existingSecrets = secrets.getItems().stream() + .collect(Collectors.toMap(s -> s.getSecretName(), s -> s.getId())); + + for (Map.Entry entry : propGen.getVaultSecrets().entrySet()) { + String secretName = entry.getKey(); + String base64Content = Base64.getEncoder().encodeToString(entry.getValue().getBytes(StandardCharsets.UTF_8)); + + SecretContentDetails contentDetails = Base64SecretContentDetails.builder() + .content(base64Content) + .stage(SecretContentDetails.Stage.Current).build(); + if (existingSecrets.containsKey(secretName)) { + h.progress(Bundle.UpdatingSecret(secretName)); + UpdateSecretDetails updateSecretDetails = UpdateSecretDetails.builder() + .secretContent(contentDetails) + .build(); + UpdateSecretRequest request = UpdateSecretRequest.builder() + .secretId(existingSecrets.get(secretName)) + .updateSecretDetails(updateSecretDetails) + .build(); + try { + UpdateSecretResponse response = client.updateSecret(request); + } catch (BmcException ex) { + // Update fails if the new value is same as the current one. It is safe to ignore + LOG.log(Level.WARNING, "Update of secret failed", ex); + } + } else { + h.progress(Bundle.CreatingSecret(secretName)); + CreateSecretDetails createDetails = CreateSecretDetails.builder() + .secretName(secretName) + .secretContent(contentDetails) + .secretRules(new ArrayList<>(Arrays.asList(SecretReuseRule.builder() + .isEnforcedOnDeletedSecretVersions(false).build()))) + .compartmentId(vault.getCompartmentId()) + .vaultId(vault.getKey().getValue()) + .keyId(key.getKey().getValue()) + .build(); + CreateSecretRequest request = CreateSecretRequest + .builder() + .createSecretDetails(createDetails) + .build(); + client.createSecret(request); + } + } + + // Add Vault dependency to the project + try { + DependencyUtils.addDependency(project, "io.micronaut.oraclecloud", "micronaut-oraclecloud-vault"); + } catch (IllegalStateException e) { + LOG.log(Level.INFO, "Unable to add Vault dependency", e); + } + } + + protected static String updateProperties(String configmap, PropertiesGenerator propGen) { + StringWriter output = new StringWriter(); + String[] lines = configmap.split("\n"); + int previousIndent = 0; + Map path = new LinkedHashMap<>(); + String propertiesName = null; + Map properties = new LinkedHashMap<>(); + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + if (line.trim().startsWith("#") || line.isEmpty()) { + output.append(line); + output.append("\n"); + continue; + } + int indent = 0; + while (line.charAt(indent) == ' ') { + indent++; + } + if (previousIndent > indent || (propertiesName != null && !line.contains("="))) { + final int f = indent; + path.entrySet().removeIf(entry -> entry.getKey() >= f); + if (propertiesName != null) { + int propIndent = previousIndent; + if (properties.isEmpty()) { + propIndent = indent + 2; + } + output.append( + formatProperties(propertiesName, properties, propIndent, propGen)); + + properties.clear(); + } + propertiesName = null; + if (line.trim().equals("---")) { //NOI18N + output.append(line); + output.append("\n"); + continue; + } + } + if (propertiesName == null) { + if (line.indexOf(':') < 0) { + throw new IllegalStateException("Invalid ConfigMap format"); //NOI18N + } + String k = line.substring(0, line.indexOf(':')).trim(); + String v = line.substring(line.indexOf(':') + 1).trim(); + if (k == null) { + throw new IllegalStateException(); + } + + path.put(indent, k); + output.append(line); + output.append("\n"); + if (v.trim().equals("|")) { + propertiesName = k; + continue; + } + } + if (propertiesName != null && line.contains("=")) { + properties.put(line.substring(0, line.indexOf('=')).trim(), + line.substring(line.indexOf('=') + 1).trim()); + } + previousIndent = indent; + } + output.append(formatProperties(propertiesName, properties, previousIndent, propGen)); + + return output.toString(); + } + + + private static String formatProperties(String proprtiesName, Map prop, int indent, PropertiesGenerator propGen) { + StringBuilder output = new StringBuilder(); + prop.entrySet().removeIf(entry -> propGen.getAllPropertiesNames().contains(entry.getKey())); + if (proprtiesName.startsWith("bootstrap")) { // NOI18N + prop.putAll(propGen.getBootstrap()); + } else if (proprtiesName.startsWith("application")) { // NOI18N + prop.putAll(propGen.getApplication()); + } + for (Map.Entry entry : prop.entrySet()) { + output.append(new String(new char[indent]).replace('\0', ' ')); + output.append(entry.getKey()); + output.append("="); + output.append(entry.getValue()); + output.append("\n"); + } + return output.toString(); + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletAction.java index e3e2f3164236..7daa765138c6 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletAction.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletAction.java @@ -149,8 +149,9 @@ void addConnection(DownloadWalletDialog.WalletInfo p) { } } Properties props = new Properties(); - props.put("OCID", p.getOcid()); //NOI18N - props.put("CompartmentOCID", p.getComaprtmentId()); //NOI18N + props.put("OCID", p.getDb().getKey().getValue()); //NOI18N + props.put("CompartmentOCID", p.getDb().getCompartmentId()); //NOI18N + props.put("Description", p.getDb().getDescription()); String dbUrl = MessageFormat.format(URL_TEMPLATE, connectionName, BaseUtilities.escapeParameters(new String[] { walletPath.toString() })); DatabaseConnection dbConn = DatabaseConnection.create( drivers[0], diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletDialog.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletDialog.java index 8b84c7d42209..ae5dc56ee182 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletDialog.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/actions/DownloadWalletDialog.java @@ -18,17 +18,16 @@ */ package org.netbeans.modules.cloud.oracle.actions; -import org.netbeans.modules.cloud.oracle.items.OCIItem; import java.awt.Dialog; import java.awt.GraphicsEnvironment; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Locale; import java.util.Optional; import javax.swing.JFileChooser; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import org.netbeans.modules.cloud.oracle.database.DatabaseItem; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; @@ -74,7 +73,7 @@ final class DownloadWalletDialog extends AbstractPasswordPanel { dbPasswordField.getDocument().addDocumentListener(docListener); } - static Optional showDialog(OCIItem db) { + static Optional showDialog(DatabaseItem db) { File home = new File(System.getProperty("user.home")); //NOI18N String lastUsedDir = NbPreferences.forModule(DownloadWalletAction.class).get(LAST_USED_DIR, home.getAbsolutePath()); //NOI18N @@ -92,7 +91,7 @@ static Optional showDialog(OCIItem db) { String dbUser = dlgPanel.dbUserField.getText(); char[] dbPasswd = dlgPanel.dbPasswordField.getPassword(); NbPreferences.forModule(DownloadWalletAction.class).put(LAST_USED_DIR, path); //NOI18N - return Optional.of(new WalletInfo(path, generatePassword(), dbUser, dbPasswd, db.getKey().getValue(), db.getCompartmentId())); + return Optional.of(new WalletInfo(path, generatePassword(), dbUser, dbPasswd, db)); } } else { try { @@ -111,7 +110,7 @@ static Optional showDialog(OCIItem db) { return Optional.empty(); } char[] password = inp.getInputText().toCharArray(); - return Optional.of(new WalletInfo(walletsDir.getAbsolutePath(), generatePassword(), username, password, db.getKey().getValue(), db.getCompartmentId())); + return Optional.of(new WalletInfo(walletsDir.getAbsolutePath(), generatePassword(), username, password, (DatabaseItem) db)); } catch (IOException ex) { Exceptions.printStackTrace(ex); } @@ -306,16 +305,14 @@ static class WalletInfo { private final char[] walletPassword; private final String dbUser; private char[] dbPassword; - private final String ocid; - private final String comaprtmentId; + private final DatabaseItem db; - public WalletInfo(String path, char[] walletPassword, String dbUser, char[] dbPassword, String ocid, String comaprtmentOcid) { + public WalletInfo(String path, char[] walletPassword, String dbUser, char[] dbPassword, DatabaseItem db) { this.path = path; this.walletPassword = walletPassword; this.dbUser = dbUser; this.dbPassword = dbPassword; - this.ocid = ocid; - this.comaprtmentId = comaprtmentOcid; + this.db = db; } public String getPath() { @@ -334,12 +331,8 @@ public char[] getDbPassword() { return dbPassword; } - public String getOcid() { - return ocid; - } - - public String getComaprtmentId() { - return comaprtmentId; + public DatabaseItem getDb() { + return db; } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Step.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AbstractStep.java similarity index 69% rename from enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Step.java rename to enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AbstractStep.java index 3bb8721dd13c..a30e56c2e2f1 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Step.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AbstractStep.java @@ -18,25 +18,23 @@ */ package org.netbeans.modules.cloud.oracle.assets; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; import org.openide.NotifyDescriptor; -import org.openide.util.Lookup; /** * * @author Jan Horvath */ -public interface Step { - - Step prepare(T item, Lookup lookup); - - NotifyDescriptor createInput(); +public abstract class AbstractStep { //implements Step { + + public void prepare(ProgressHandle handle, Values values) { } - boolean onlyOneChoice(); + public abstract NotifyDescriptor createInput(); - Step getNext(); + public abstract boolean onlyOneChoice(); - void setValue(String selected); + public abstract void setValue(String selected); - U getValue(); - + public abstract U getValue(); } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddNewAssetCommand.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddNewAssetCommand.java index c98adcfce38a..722fbad39d1e 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddNewAssetCommand.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddNewAssetCommand.java @@ -18,6 +18,9 @@ */ package org.netbeans.modules.cloud.oracle.assets; +import org.netbeans.modules.cloud.oracle.steps.SuggestedStep; +import org.netbeans.modules.cloud.oracle.steps.ProjectStep; +import org.netbeans.modules.cloud.oracle.steps.CompartmentStep; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -27,22 +30,20 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import org.netbeans.api.project.Project; -import org.netbeans.modules.cloud.oracle.assets.Steps.ItemTypeStep; +import org.netbeans.modules.cloud.oracle.actions.AddADBAction; +import org.netbeans.modules.cloud.oracle.database.DatabaseItem; +import org.netbeans.modules.cloud.oracle.steps.ItemTypeStep; import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.steps.DatabaseConnectionStep; +import org.netbeans.modules.cloud.oracle.steps.TenancyStep; import org.netbeans.spi.lsp.CommandProvider; -import org.openide.util.Lookup; -import org.openide.util.NbBundle; -import org.openide.util.Pair; +import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ServiceProvider; /** * * @author Jan Horvath */ -@NbBundle.Messages({ - "NoProjects=No Project Found", - "SelectProject=Select Project to Update Dependencies", - "SelectResourceType=Select Resource Type"}) @ServiceProvider(service = CommandProvider.class) public class AddNewAssetCommand implements CommandProvider { @@ -68,20 +69,42 @@ public Set getCommands() { @Override public CompletableFuture runCommand(String command, List arguments) { CompletableFuture future = new CompletableFuture(); + Steps.NextStepProvider nsProvider = Steps.NextStepProvider.builder() + .stepForClass(ItemTypeStep.class, (s) -> { + if ("Databases".equals(s.getValue())) { + return new DatabaseConnectionStep(); + } + return new TenancyStep(); + }).stepForClass(TenancyStep.class, (s) -> new CompartmentStep()) + .stepForClass(CompartmentStep.class, (s) -> new SuggestedStep(null)) + .stepForClass(SuggestedStep.class, (s) -> new ProjectStep()) + .build(); Steps.getDefault() - .executeMultistep(new ItemTypeStep(), Lookup.EMPTY) - .thenAccept(result -> { - Project project = ((Pair) result).first(); - OCIItem item = ((Pair) result).second(); + .executeMultistep(new ItemTypeStep(), Lookups.fixed(nsProvider)) + .thenAccept(values -> { + Project project = values.getValueForStep(ProjectStep.class); + OCIItem item; + if ("Databases".equals(values.getValueForStep(ItemTypeStep.class))) { + item = values.getValueForStep(DatabaseConnectionStep.class); + if (item == null) { + item = new AddADBAction().addADB(); + } + } else { + item = values.getValueForStep(SuggestedStep.class); + } + if (item == null) { + future.cancel(true); + return; + } CloudAssets.getDefault().addItem(item); String[] art = DEP_MAP.get(item.getKey().getPath()); try { DependencyUtils.addDependency(project, art[0], art[1]); + future.complete(null); } catch (IllegalStateException e) { future.completeExceptionally(e); } }); - future.complete(null); return future; } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddSuggestedItemAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddSuggestedItemAction.java index 6e6a7815fb1e..80bf066e2d54 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddSuggestedItemAction.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddSuggestedItemAction.java @@ -18,18 +18,15 @@ */ package org.netbeans.modules.cloud.oracle.assets; +import org.netbeans.modules.cloud.oracle.steps.SuggestedStep; +import org.netbeans.modules.cloud.oracle.steps.CompartmentStep; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.HashMap; -import java.util.Map; import java.util.logging.Logger; -import org.netbeans.api.db.explorer.ConnectionManager; -import org.netbeans.api.db.explorer.DatabaseConnection; import org.netbeans.modules.cloud.oracle.actions.AddADBAction; -import org.netbeans.modules.cloud.oracle.assets.Steps.DatabaseConnectionStep; -import org.netbeans.modules.cloud.oracle.assets.Steps.TenancyStep; +import org.netbeans.modules.cloud.oracle.steps.DatabaseConnectionStep; +import org.netbeans.modules.cloud.oracle.steps.TenancyStep; import org.netbeans.modules.cloud.oracle.database.DatabaseItem; -import org.netbeans.modules.cloud.oracle.items.OCID; import org.netbeans.modules.cloud.oracle.items.OCIItem; import org.openide.awt.ActionID; import org.openide.awt.ActionRegistration; @@ -52,20 +49,11 @@ @NbBundle.Messages({ "AddSuggestedItem=Add Suggested Oracle Cloud Resource", - "SelectProfile=Select OCI Profile", "# {0} - tenancy name", "# {1} - region id", - "SelectProfile_Description={0} (region: {1})", - "SelectCompartment=Select Compartment", - "NoProfile=There is not any OCI profile in the config", "NoCompartment=There are no compartments in the Tenancy", "CollectingProfiles=Searching for OCI Profiles", - "CollectingProfiles_Text=Loading OCI Profiles", - "CollectingItems=Loading OCI contents", - "CollectingItems_Text=Listing compartments and databases", - "SelectItem=Select {0}", - "SelectDBConnection=Select Database Connection" -}) + "CollectingItems=Loading OCI contents",}) public class AddSuggestedItemAction implements ActionListener { private static final Logger LOG = Logger.getLogger(AddADBAction.class.getName()); @@ -79,35 +67,26 @@ public AddSuggestedItemAction(SuggestedItem context) { @Override public void actionPerformed(ActionEvent e) { if ("Databases".equals(context.getPath())) { //NOI18N - Map adbConnections = new HashMap<>(); - DatabaseConnection[] connections = ConnectionManager.getDefault().getConnections(); - for (int i = 0; i < connections.length; i++) { - String name = connections[i].getDisplayName(); - String ocid = connections[i].getConnectionProperties().getProperty("OCID"); //NOI18N - String compartmentId = connections[i].getConnectionProperties().getProperty("CompartmentOCID"); //NOI18N - if (ocid != null && compartmentId != null) { - DatabaseItem dbItem - = new DatabaseItem(OCID.of(ocid, "Databases"), compartmentId, name, null, name); //NOI18N - adbConnections.put(name, dbItem); - } - } - if (adbConnections.isEmpty()) { - DatabaseItem db = new AddADBAction().addADB(); - if (db != null) { - CloudAssets.getDefault().addItem(db); - } - } else { - Steps.getDefault().executeMultistep(new DatabaseConnectionStep(adbConnections), Lookup.EMPTY) - .thenAccept(result -> CloudAssets.getDefault().addItem((OCIItem) result)); - } + Steps.getDefault().executeMultistep(new DatabaseConnectionStep(), Lookup.EMPTY) + .thenAccept(values -> { + DatabaseItem db = values.getValueForStep(DatabaseConnectionStep.class); + if (db == null) { + db = new AddADBAction().addADB(); + } + CloudAssets.getDefault().addItem(db); + }); return; } Steps.NextStepProvider nsProvider = Steps.NextStepProvider.builder() - .stepForClass(Steps.CompartmentStep.class, (s) -> new Steps.SuggestedStep(context.getPath())) + .stepForClass(TenancyStep.class, (s) -> new CompartmentStep()) + .stepForClass(CompartmentStep.class, (s) -> new SuggestedStep(context.getPath())) .build(); Lookup lookup = Lookups.fixed(nsProvider); Steps.getDefault().executeMultistep(new TenancyStep(), lookup) - .thenAccept(result -> CloudAssets.getDefault().addItem((OCIItem) result)); + .thenAccept(values -> { + OCIItem item = values.getValueForStep(SuggestedStep.class); + CloudAssets.getDefault().addItem(item); + }); } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddToProject.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddToProject.java new file mode 100644 index 000000000000..e7d2fd0cafeb --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/AddToProject.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.assets; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.openide.awt.ActionID; +import org.openide.awt.ActionRegistration; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@ActionID( + category = "Tools", + id = "org.netbeans.modules.cloud.oracle.actions.AddToProject" +) +@ActionRegistration( + displayName = "#AddToProject", + asynchronous = true +) + +@NbBundle.Messages({ + "AddToProject=Add to Oracle Cloud Assets" +}) +public class AddToProject implements ActionListener { + + private final OCIItem context; + + public AddToProject(OCIItem context) { + this.context = context; + } + + @Override + public void actionPerformed(ActionEvent e) { + CloudAssets.getDefault().addItem(context); + } + + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CloudAssets.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CloudAssets.java index b9cf8b17c670..34b502b49b5e 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CloudAssets.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CloudAssets.java @@ -29,14 +29,21 @@ import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.StringReader; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -44,7 +51,6 @@ import javax.swing.event.ChangeListener; import org.netbeans.api.db.explorer.ConnectionManager; import org.netbeans.api.db.explorer.DatabaseConnection; -import org.netbeans.modules.cloud.oracle.OCIChildFactory; import org.netbeans.modules.cloud.oracle.bucket.BucketItem; import org.netbeans.modules.cloud.oracle.compute.ClusterItem; import org.netbeans.modules.cloud.oracle.compute.ComputeInstanceItem; @@ -60,10 +66,13 @@ import org.openide.util.Parameters; /** + * Maintains a set of OCI resources that are either assigned or recommended for + * assignment to a workspace. * * @author Jan Horvath */ public final class CloudAssets { + private static final Logger LOG = Logger.getLogger(CloudAssets.class.getName()); private static final String SUGGESTED = "Suggested"; //NOI18N @@ -73,7 +82,9 @@ public final class CloudAssets { private boolean assetsLoaded = false; private Set items = new HashSet<>(); - private Set suggested = new HashSet<>(); + private final Set suggested = new HashSet<>(); + + private final Map refNames = new HashMap<>(); private final ChangeSupport changeSupport; private final Gson gson; @@ -90,7 +101,7 @@ public final class CloudAssets { for (int i = 0; i < connections.length; i++) { String ocid = (String) connections[i].getConnectionProperties().get("OCID"); //NOI18N if (ocid != null) { - ocids.add(ocid); + ocids.add(ocid); } } boolean update = false; @@ -115,25 +126,36 @@ public static synchronized CloudAssets getDefault() { return instance; } - public void addItem(OCIItem newItem) { + public synchronized void addItem(OCIItem newItem) { Parameters.notNull("newItem cannot be null", newItem); - items.add(newItem); - update(); - storeAssets(); + long presentCount = items.stream() + .filter(i -> i.getKey().getPath().equals(newItem.getKey().getPath())) + .count(); + if (newItem.maxInProject() > presentCount) { + items.add(newItem); + update(); + storeAssets(); + } } - void removeItem(OCIItem item) { + synchronized void removeItem(OCIItem item) { + boolean update = false; + if (refNames.remove(item) != null) { + update = true; + } if (items.remove(item)) { - update(); + update = true; + } + if (update) { storeAssets(); + update(); } } - public void update() { + void update() { OpenProjectsFinder.getDefault().findOpenProjects().thenAccept(projects -> { SuggestionAnalyzer analyzer = new DependenciesAnalyzer(); - Set suggested = analyzer.findSuggestions(projects); - setSuggestions(suggested); + setSuggestions(analyzer.findSuggestions(projects)); }); } @@ -159,12 +181,60 @@ private synchronized void setSuggestions(Set newSuggested) { changeSupport.fireChange(); } - public List getItems() { + /** + * Returns a Collection of all items, including suggested + * items. + * + * @return {@link Collection} of {@link OCIItem} + */ + public Collection getItems() { List list = new ArrayList<>(suggested); list.addAll(items); return list; } + /** + * Returns a Collection of items assigned by user. This doesn't + * include suggested items. + * + * @return {@link Collection} of {@link OCIItem} + */ + public Collection getAssignedItems() { + return Collections.unmodifiableCollection(items); + } + + public boolean setReferenceName(OCIItem item, String refName) { + Parameters.notNull("refName", refName); //NOI18N + Parameters.notNull("OCIItem", item); //NOI18N + for (Entry refEntry : refNames.entrySet()) { + if (refEntry.getKey().getKey().getPath().equals(item.getKey().getPath()) + && refName.equals(refEntry.getValue())) { + return false; + } + } + String oldRefName = refNames.get(item); + refNames.put(item, refName); + storeAssets(); + item.fireRefNameChanged(oldRefName, refName); +// item.fireRefNameChanged(null, refName); + return true; + } + + public String getReferenceName(OCIItem item) { + return refNames.get(item); + } + + private void setReferenceName(String ocid, String refName) { + for (OCIItem item : items) { + if (item.getKey().getValue().equals(ocid)) { + refNames.put(item, refName); + storeAssets(); + item.fireRefNameChanged(null, refName); + return; + } + } + } + /** * Adds a ChangeListener to the listener list. * @@ -187,9 +257,6 @@ synchronized void storeAssets() { if (!assetsLoaded) { return; } - Set toStore = items.stream() - .filter(i -> !SUGGESTED.equals(i.getKey().getPath())) //NOI18N - .collect(Collectors.toSet()); FileObject file = null; try { FileObject fo = FileUtil.createFolder(FileUtil.getConfigRoot(), CLOUD_ASSETS_PATH); @@ -202,9 +269,25 @@ synchronized void storeAssets() { } if (file != null) { try (FileLock lock = file.lock()) { - OutputStream os = file.getOutputStream(lock); - os.write(gson.toJson(toStore).getBytes()); - os.close(); + OutputStream os = file.getOutputStream(lock); + JsonWriter jsonWriter = gson.newJsonWriter(new OutputStreamWriter(os)); + jsonWriter.beginObject(); + jsonWriter.name("items").beginArray(); //NOI18N + for (OCIItem item : items) { + gson.toJson(item, item.getClass(), jsonWriter); + } + jsonWriter.endArray(); + jsonWriter.name("referenceNames").beginArray(); //NOI18N + for (Entry entry : refNames.entrySet()) { + jsonWriter.beginObject(); + jsonWriter.name("ocid").value(entry.getKey().getKey().getValue()); //NOI18N + jsonWriter.name("referenceName").value(entry.getValue()); //NOI18N + jsonWriter.endObject(); + } + jsonWriter.endArray(); + jsonWriter.endObject(); + jsonWriter.close(); + os.close(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } @@ -229,32 +312,73 @@ synchronized void loadAssets() { try (JsonReader reader = new JsonReader(new StringReader(content))) { Set loaded = new HashSet<>(); - reader.beginArray(); + Map loadingRefNames = new HashMap<>(); + reader.beginObject(); while (reader.hasNext()) { - JsonElement element = JsonParser.parseReader(reader); - String path = element.getAsJsonObject() - .get("id").getAsJsonObject() //NOI18N - .get("path").getAsString(); //NOI18N - switch (path) { - case "Databases": //NOI18N - loaded.add(gson.fromJson(element, DatabaseItem.class)); - break; - case "Bucket": //NOI18N - loaded.add(gson.fromJson(element, BucketItem.class)); - break; - case "Cluster": //NOI18N - loaded.add(gson.fromJson(element, ClusterItem.class)); + String rootObjName = reader.nextName(); + switch (rootObjName) { + case "items": //NOI18N + reader.beginArray(); + while (reader.hasNext()) { + JsonElement element = JsonParser.parseReader(reader); + String path = element.getAsJsonObject() + .get("id").getAsJsonObject() //NOI18N + .get("path").getAsString(); //NOI18N + switch (path) { + case "Databases": //NOI18N + loaded.add(gson.fromJson(element, DatabaseItem.class)); + break; + case "Bucket": //NOI18N + loaded.add(gson.fromJson(element, BucketItem.class)); + break; + case "Cluster": //NOI18N + loaded.add(gson.fromJson(element, ClusterItem.class)); + break; + case "ComputeInstance": //NOI18N + loaded.add(gson.fromJson(element, ComputeInstanceItem.class)); + break; + case "Vault": //NOI18N + loaded.add(gson.fromJson(element, VaultItem.class)); + break; + } + } + reader.endArray(); break; - case "ComputeInstance": //NOI18N - loaded.add(gson.fromJson(element, ComputeInstanceItem.class)); - break; - case "Vault": //NOI18N - loaded.add(gson.fromJson(element, VaultItem.class)); + case "referenceNames": //NOI18N + reader.beginArray(); + while (reader.hasNext()) { + reader.beginObject(); + String refOcid = null; + String refName = null; + while (reader.hasNext()) { + String name = reader.nextName(); + switch (name) { + case "ocid": + refOcid = reader.nextString(); + break; + case "referenceName": + refName = reader.nextString(); + break; + default: + reader.skipValue(); + break; + } + if (refOcid != null && refName != null) { + loadingRefNames.put(refOcid, refName); + } + } + reader.endObject(); + } + reader.endArray(); break; } } - reader.endArray(); + reader.endObject(); items = loaded; + for (Entry entry : loadingRefNames.entrySet()) { + setReferenceName(entry.getKey(), entry.getValue()); + } + } catch (JsonIOException | JsonSyntaxException | IOException e) { LOG.log(Level.INFO, "Unable to load assets", e); } finally { @@ -263,7 +387,6 @@ synchronized void loadAssets() { } private static final class OCIDDeserializer implements JsonDeserializer { - @Override public OCID deserialize(JsonElement json, Type type, JsonDeserializationContext jdc) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreatePoliciesCommand.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreatePoliciesCommand.java index 84630bbd12aa..19c1bfe434ef 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreatePoliciesCommand.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreatePoliciesCommand.java @@ -27,25 +27,19 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; import org.netbeans.modules.cloud.oracle.policy.PolicyGenerator; import org.netbeans.spi.lsp.CommandProvider; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; -import org.openide.util.Exceptions; import org.openide.util.RequestProcessor; import org.openide.util.lookup.ServiceProvider; /** - * + * * @author Jan Horvath */ @ServiceProvider(service = CommandProvider.class) public class CreatePoliciesCommand implements CommandProvider { - private static final Logger LOG = Logger.getLogger(CreatePoliciesCommand.class.getName()); - - - private static final String COMMAND_CREATE_POLICIES = "nbls.cloud.assets.policy.create.local"; //NOI18N private static final String COMMAND_CREATE_CONFIG = "nbls.cloud.assets.config.create.local"; //NOI18N private static final String COMMAND_CLOUD_ASSETS_REFRESH = "nbls.cloud.assets.refresh"; //NOI18N @@ -81,7 +75,9 @@ public CompletableFuture runCommand(String command, List argumen } else if (COMMAND_CREATE_CONFIG.equals(command)) { StringWriter writer = new StringWriter(); Properties dbProps = new Properties(); - dbProps.putAll(PropertiesGenerator.getProperties(null)); + PropertiesGenerator propGen = new PropertiesGenerator(false); + dbProps.putAll(propGen.getApplication()); + dbProps.putAll(propGen.getBootstrap()); try { dbProps.store(writer, "Generated application.properties\n" //NOI18N + "Uncomment following line when running inside Oracle Cloud\n" //NOI18N diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/OCIPropertiesProvider.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/OCIPropertiesProvider.java index afb6e45e04c1..89f469b050c9 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/OCIPropertiesProvider.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/OCIPropertiesProvider.java @@ -51,7 +51,6 @@ import org.netbeans.api.db.explorer.ConnectionManager; import org.netbeans.api.db.explorer.DatabaseConnection; import org.netbeans.spi.lsp.CommandProvider; -import org.openide.util.Lookup; import org.openide.util.lookup.ServiceProvider; /** @@ -85,7 +84,9 @@ public CompletableFuture runCommand(String command, List argumen Properties dbProps = new Properties(); DatabaseConnection conn = ConnectionManager.getDefault().getPreferredConnection(true); - dbProps.putAll(PropertiesGenerator.getProperties(null)); + PropertiesGenerator propGen = new PropertiesGenerator(true); + dbProps.putAll(propGen.getApplication()); + dbProps.putAll(propGen.getBootstrap()); // If cloud assets are empty, try to get properties for a preferred DB connection if (conn != null && dbProps.isEmpty()) { dbProps.put("datasources.default.url", conn.getDatabaseURL()); //NOI18N diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/PropertiesGenerator.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/PropertiesGenerator.java index 5e863e70eab1..d6b86efa2f85 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/PropertiesGenerator.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/PropertiesGenerator.java @@ -18,12 +18,18 @@ */ package org.netbeans.modules.cloud.oracle.assets; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.UUID; import org.netbeans.api.db.explorer.ConnectionManager; import org.netbeans.api.db.explorer.DatabaseConnection; import org.netbeans.modules.cloud.oracle.bucket.BucketItem; import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.vault.VaultItem; /** * @@ -31,20 +37,48 @@ */ public final class PropertiesGenerator { - private PropertiesGenerator() { - } + private static final String DEFAULT = "DEFAULT"; + final Map application = new HashMap<>(); + final Map bootstrap = new HashMap<>(); + final Map secrets = new HashMap<>(); + final List allPropertiesNames = Arrays.asList( + "micronaut.object-storage.oracle-cloud.default.bucket", + "micronaut.object-storage.oracle-cloud.default.namespace", + "datasources.default.dialect", + "datasources.default.ocid", + "datasources.default.walletPassword", + "datasources.default.username", + "datasources.default.password", + "datasources.default.url", + "oci.config.instance-principal.enabled", + "oci.vault.config.enabled", + "oci.vault.vaults[0].ocid", + "oci.vault.vaults[0].compartment-ocid" + ); - public static Map getProperties(String environment) { - Map result = new HashMap<>(); - for (OCIItem item : CloudAssets.getDefault().getItems()) { + public PropertiesGenerator(boolean local) { + Collection items = CloudAssets.getDefault().getItems(); + VaultItem vault = null; + DatabaseConnection conn = null; + for (OCIItem item : items) { + if ("Vault".equals(item.getKey().getPath())) { + vault = (VaultItem) item; + } + } + for (OCIItem item : items) { + String refName = CloudAssets.getDefault().getReferenceName(item); + if (refName == null) { + refName = DEFAULT; + } + String nameLowercase = refName.toLowerCase(); + String refNameUppercase = refName.toUpperCase(); switch (item.getKey().getPath()) { case "Bucket": //NOI18N - result.put("micronaut.object-storage.oracle-cloud.default.bucket", item.getKey().getValue()); //NOI18N - result.put("micronaut.object-storage.oracle-cloud.default.namespace", ((BucketItem) item).getNamespace()); //NOI18N + application.put("micronaut.object-storage.oracle-cloud." + nameLowercase + ".bucket", item.getKey().getValue()); //NOI18N + application.put("micronaut.object-storage.oracle-cloud." + nameLowercase + ".namespace", ((BucketItem) item).getNamespace()); //NOI18N break; case "Databases": //NOI18N DatabaseConnection[] connections = ConnectionManager.getDefault().getConnections(); - DatabaseConnection conn = null; for (int i = 0; i < connections.length; i++) { if (item.getKey().getValue().equals( connections[i].getConnectionProperties().get("OCID"))) { //NOI18N @@ -53,22 +87,68 @@ public static Map getProperties(String environment) { } } if (conn != null) { - result.put("datasources.default.url", conn.getDatabaseURL()); //NOI18N - result.put("datasources.default.username", conn.getUser()); //NOI18N - result.put("datasources.default.password", conn.getPassword()); //NOI18N - result.put("datasources.default.driverClassName", conn.getDriverClass()); //NOI18N - String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N - if (ocid != null && !ocid.isEmpty()) { - result.put("datasources.default.ocid", ocid); //NOI18N + + if (vault != null) { + application.put("datasources." + nameLowercase + ".dialect", "ORACLE"); // NOI18N + application.put("datasources." + nameLowercase + ".ocid", "${DATASOURCES_" + refNameUppercase + "_OCID}"); // NOI18N + application.put("datasources." + nameLowercase + ".walletPassword", "${DATASOURCES_" + refNameUppercase + "_WALLET_PASSWORD}"); // NOI18N + application.put("datasources." + nameLowercase + ".username", "${DATASOURCES_" + refNameUppercase + "_USERNAME}"); // NOI18N + application.put("datasources." + nameLowercase + ".password", "${DATASOURCES_" + refNameUppercase + "_PASSWORD}"); // NOI18N + putSecret(refNameUppercase, "Username", conn.getUser()); //NOI18N + putSecret(refNameUppercase, "Password", conn.getPassword()); //NOI18N + putSecret(refNameUppercase, "OCID", (String) conn.getConnectionProperties().get("OCID")); //NOI18N + putSecret(refNameUppercase, "CompartmentOCID", (String) conn.getConnectionProperties().get("CompartmentOCID")); //NOI18N + putSecret(refNameUppercase, "wallet_Password", UUID.randomUUID().toString()); //NOI18N + } else { + if (local) { + application.put("datasources." + nameLowercase + ".url", conn.getDatabaseURL()); //NOI18N + } + application.put("datasources." + nameLowercase + ".username", conn.getUser()); //NOI18N + application.put("datasources." + nameLowercase + ".password", conn.getPassword()); //NOI18N + application.put("datasources." + nameLowercase + ".walletPassword", UUID.randomUUID().toString()); //NOI18N + application.put("datasources." + nameLowercase + ".driverClassName", conn.getDriverClass()); //NOI18N + String ocid = (String) conn.getConnectionProperties().get("OCID"); //NOI18N + if (ocid != null && !ocid.isEmpty()) { + application.put("datasources." + nameLowercase + ".ocid", ocid); //NOI18N + } } } - break; - case "Vault": - result.put("oci.vault.vaults[0].ocid", item.getKey().getValue()); //NOI18N - result.put("oci.vault.vaults[0].compartment-ocid", item.getCompartmentId()); //NOI18N } } - return result; + if (!local) { + bootstrap.put("oci.config.instance-principal.enabled", "true"); // NOI18N + } + if (vault != null) { + bootstrap.put("oci.vault.config.enabled", "true"); // NOI18N + bootstrap.put("oci.vault.vaults[0].ocid", vault.getKey().getValue()); //NOI18N + bootstrap.put("oci.vault.vaults[0].compartment-ocid", vault.getCompartmentId()); //NOI18N + } + } + + private void putSecret(String datasourceName, String name, String value) { + String secretName = "DATASOURCES_" + datasourceName + "_" + name; //NOI18N + secrets.put(secretName, value); + } + + /** + * Returns all potentially used property names. This can be useful for cleaning up unused values, such as those in a Config Map. + * + * @return {@link Collection} + */ + public Collection getAllPropertiesNames() { + return allPropertiesNames; + } + + public synchronized Map getApplication() { + return Collections.unmodifiableMap(application); + } + + public synchronized Map getBootstrap() { + return Collections.unmodifiableMap(bootstrap); + } + + public synchronized Map getVaultSecrets() { + return Collections.unmodifiableMap(secrets); } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RemoveFromProject.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RemoveFromProject.java index de4d3e1611cc..55f5c8277b03 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RemoveFromProject.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RemoveFromProject.java @@ -39,7 +39,7 @@ ) @NbBundle.Messages({ - "RemoveFromProject=Remove From Assets" + "RemoveFromProject=Remove From Oracle Cloud Assets" }) public class RemoveFromProject implements ActionListener { @@ -54,5 +54,4 @@ public void actionPerformed(ActionEvent e) { CloudAssets.getDefault().removeItem(context); } - } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetReferenceNameAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetReferenceNameAction.java new file mode 100644 index 000000000000..6cd2a6b651c4 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SetReferenceNameAction.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.assets; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.awt.ActionID; +import org.openide.awt.ActionRegistration; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@ActionID( + category = "Tools", + id = "org.netbeans.modules.cloud.oracle.actions.SetReferenceName" +) +@ActionRegistration( + displayName = "#ReferenceName", + asynchronous = true +) + +@NbBundle.Messages({ + "ReferenceName=Set a reference name", + "ReferenceNameValidationError=Reference Name can contain only letters and numbers", + "ReferenceNameSame=Reference names must be unique for each resource type." +}) +public class SetReferenceNameAction implements ActionListener { + + private final OCIItem context; + + public SetReferenceNameAction(OCIItem context) { + this.context = context; + } + + @Override + public void actionPerformed(ActionEvent e) { + String oldRefName = CloudAssets.getDefault().getReferenceName(context); + if (oldRefName == null) { + oldRefName = ""; + } + NotifyDescriptor.InputLine inp = new NotifyDescriptor.InputLine(oldRefName, Bundle.ReferenceName()); + Object selected = DialogDisplayer.getDefault().notify(inp); + if (DialogDescriptor.OK_OPTION != selected) { + return; + } + String refName = inp.getInputText(); + if (refName.matches("[a-zA-Z0-9]+")) { //NOI18N + if (!CloudAssets.getDefault().setReferenceName(context, refName)) { + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.ReferenceNameSame()); + DialogDisplayer.getDefault().notify(msg); + } + } else { + NotifyDescriptor.Message msg = new NotifyDescriptor.Message(Bundle.ReferenceNameValidationError()); + DialogDisplayer.getDefault().notify(msg); + } + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Steps.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Steps.java index fe3f4c19fa2c..9f7c01ae924d 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Steps.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/Steps.java @@ -18,52 +18,21 @@ */ package org.netbeans.modules.cloud.oracle.assets; -import com.oracle.bmc.identity.Identity; -import com.oracle.bmc.identity.IdentityClient; -import com.oracle.bmc.identity.model.Compartment; -import com.oracle.bmc.identity.model.Tenancy; -import com.oracle.bmc.identity.requests.ListCompartmentsRequest; -import com.oracle.bmc.identity.responses.ListCompartmentsResponse; -import com.oracle.bmc.model.BmcException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.MissingResourceException; -import java.util.Optional; -import java.util.TreeMap; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.project.Project; -import org.netbeans.api.project.ProjectInformation; -import org.netbeans.api.project.ProjectUtils; -import org.netbeans.modules.cloud.oracle.OCIManager; -import org.netbeans.modules.cloud.oracle.OCIProfile; -import org.netbeans.modules.cloud.oracle.OCISessionInitiator; -import org.netbeans.modules.cloud.oracle.bucket.BucketNode; -import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; -import org.netbeans.modules.cloud.oracle.compute.ClusterNode; -import org.netbeans.modules.cloud.oracle.compute.ComputeInstanceNode; -import org.netbeans.modules.cloud.oracle.database.DatabaseItem; -import org.netbeans.modules.cloud.oracle.database.DatabaseNode; -import org.netbeans.modules.cloud.oracle.items.OCID; import org.netbeans.modules.cloud.oracle.items.OCIItem; -import org.netbeans.modules.cloud.oracle.items.TenancyItem; -import org.netbeans.modules.cloud.oracle.vault.VaultNode; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; -import org.openide.util.Pair; import org.openide.util.RequestProcessor; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; @@ -74,11 +43,10 @@ * @author Jan Horvath */ @NbBundle.Messages({ - "Databases=Oracle Autonomous Database", - "Vault=OCI Vault", - "Bucket=Object Storage Bucket", - "Cluster=Oracle Container Engine", - "Compute=Compute Instance" + "MSG_CollectingItems=Loading OCI contents", + "FetchingDevopsProjects=Fetching DevOps projects", + "SelectItem=Select {0}", + "LoadingItems=Loading items for the next step" }) public final class Steps { private static final Logger LOG = Logger.getLogger(Steps.class.getName()); @@ -92,16 +60,20 @@ public static synchronized Steps getDefault() { return instance; } - public CompletableFuture executeMultistep(Step firstStep, Lookup lookup) { + public CompletableFuture executeMultistep(AbstractStep firstStep, Lookup lookup) { DialogDisplayer dd = DialogDisplayer.getDefault(); CompletableFuture future = new CompletableFuture(); RP.post(() -> { - Multistep multistep = new Multistep(firstStep, lookup); - NotifyDescriptor.ComposedInput ci = new NotifyDescriptor.ComposedInput(Bundle.AddSuggestedItem(), 3, multistep.createInput()); - if (DialogDescriptor.OK_OPTION == dd.notify(ci)) { - future.complete(multistep.getResult()); - } else { - future.cancel(true); + try { + Multistep multistep = new Multistep(firstStep, lookup); + NotifyDescriptor.ComposedInput ci = new NotifyDescriptor.ComposedInput(Bundle.AddSuggestedItem(), 3, multistep.createInput()); + if (DialogDescriptor.OK_OPTION == dd.notify(ci)) { + future.complete(multistep.getResult()); + } else { + future.cancel(true); + } + } catch (Throwable t) { + future.completeExceptionally(t); } }); return future; @@ -112,14 +84,14 @@ public CompletableFuture executeMultistep(Step firstStep, Lookup lookup) * */ public static class NextStepProvider { - private final Map, Function> steps; + private final Map, Function> steps; /** * Private constructor to initialize the NextStepProvider with a map of steps. * * @param steps a map associating classes with their corresponding {@link Step} instances */ - private NextStepProvider(Map, Function> steps) { + private NextStepProvider(Map, Function> steps) { this.steps = steps; } @@ -129,8 +101,8 @@ private NextStepProvider(Map, Function> steps) * @param currentStep the current step for which the next step is to be retrieved * @return the {@link Step} associated with the specified class, or null if no step is found */ - public Step nextStepFor(Step currentStep) { - Function nextStep = steps.get(currentStep.getClass()); + public AbstractStep nextStepFor(AbstractStep currentStep) { + Function nextStep = steps.get(currentStep.getClass()); return nextStep != null ? nextStep.apply(currentStep) : null; } @@ -147,7 +119,7 @@ public static Builder builder() { * Builder class for constructing a NextStepProvider. */ public static class Builder { - private final Map, Function> steps = new HashMap<> (); + private final Map, Function> steps = new HashMap<> (); /** * Private constructor for the Builder. @@ -162,7 +134,7 @@ private Builder() { * @param stepFunction the function to be associated with the class * @return the current Builder instance for method chaining */ - public Builder stepForClass(Class clazz, Function stepFunction) { + public Builder stepForClass(Class clazz, Function stepFunction) { steps.put(clazz, stepFunction); return this; } @@ -177,23 +149,38 @@ public NextStepProvider build() { } } } - + + /** + * Provides values for steps in the multi step dialog. + * + */ + public interface Values { + + /** + * Returns a value for a given {@link Step}. + * @param step + * @return + */ + public T getValueForStep(Class> step); + } private static class Multistep { - private final LinkedList steps = new LinkedList<>(); + private final LinkedList steps = new LinkedList<>(); private final Lookup lookup; + private final Values values; - Multistep(Step firstStep, Lookup lookup) { + Multistep(AbstractStep firstStep, Lookup lookup) { steps.add(firstStep); - this.lookup = lookup; + values = new MultistepValues(steps); + this.lookup = new ProxyLookup(Lookups.fixed(values), lookup); } NotifyDescriptor.ComposedInput.Callback createInput() { return new NotifyDescriptor.ComposedInput.Callback() { private int lastNumber = 0; - private void readValue(Step step, NotifyDescriptor desc) { + private void readValue(AbstractStep step, NotifyDescriptor desc) { String selected = null; if (!step.onlyOneChoice()) { if (desc instanceof NotifyDescriptor.QuickPick) { @@ -216,7 +203,7 @@ public NotifyDescriptor createInput(NotifyDescriptor.ComposedInput input, int nu while (steps.size() > 1) { steps.removeLast(); } - steps.getLast().prepare(null, lookup); + prepare(steps.getLast()); } else if (lastNumber > number) { steps.removeLast(); while (steps.getLast().onlyOneChoice() && steps.size() > 1) { @@ -226,12 +213,12 @@ public NotifyDescriptor createInput(NotifyDescriptor.ComposedInput input, int nu return steps.getLast().createInput(); } else { readValue(steps.getLast(), input.getInputs()[number - 2]); - steps.add(steps.getLast().getNext()); + steps.add(getNextFor(steps.getLast())); } lastNumber = number; while (steps.getLast() != null && steps.getLast().onlyOneChoice()) { - steps.add(steps.getLast().getNext()); + steps.add(getNextFor(steps.getLast())); } if (steps.getLast() == null) { steps.removeLast(); @@ -239,226 +226,56 @@ public NotifyDescriptor createInput(NotifyDescriptor.ComposedInput input, int nu } return steps.getLast().createInput(); } - }; - } - - public Object getResult() { - return steps.getLast().getValue(); - } - } - - public static final class TenancyStep implements Step { - List profiles = new LinkedList<>(); - private AtomicReference selected = new AtomicReference<>(); - private Lookup lookup; - - @Override - public NotifyDescriptor createInput() { - if (onlyOneChoice()) { - throw new IllegalStateException("No data to create input"); // NOI18N - } - String title = Bundle.SelectProfile(); - List items = new ArrayList<>(profiles.size()); - for (OCIProfile p : profiles) { - Tenancy t = p.getTenancyData(); - if (t != null) { - items.add(new NotifyDescriptor.QuickPick.Item(p.getId(), Bundle.SelectProfile_Description(t.getName(), t.getHomeRegionKey()))); - } - } - if (profiles.stream().filter(p -> p.getTenancy().isPresent()).count() == 0) { - title = Bundle.NoProfile(); - } - return new NotifyDescriptor.QuickPick(title, title, items, false); - } - - @Override - public Step getNext() { - return new CompartmentStep().prepare(getValue(), lookup); - } - - @Override - public Step prepare(Object i, Lookup lookup) { - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.CollectingProfiles()); - h.start(); - h.progress(Bundle.CollectingProfiles_Text()); - try { - profiles = OCIManager.getDefault().getConnectedProfiles(); - } finally { - h.finish(); - } - return this; - } - - public void setValue(String value) { - for (OCIProfile profile : profiles) { - if (profile.getId().equals(value)) { - profile.getTenancy().ifPresent(t -> this.selected.set(t)); - break; + + private void prepare(AbstractStep step) { + ProgressHandle h = ProgressHandle.createHandle(Bundle.LoadingItems()); + h.start(); + try { + h.progress(Bundle.LoadingItems()); + step.prepare(h, values); + } finally { + h.finish(); + } } - } - } - - @Override - public TenancyItem getValue() { - if (onlyOneChoice()) { - return profiles.stream() - .map(p -> p.getTenancy()) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst() - .get(); - } - return selected.get(); - } - - @Override - public boolean onlyOneChoice() { - return profiles.stream().filter(p -> p.getTenancy().isPresent()).count() == 1; - } - } - - public static final class CompartmentStep implements Step { - private Map compartments = null; - private CompartmentItem selected; - private Lookup lookup; - - public Step prepare(TenancyItem tenancy, Lookup lookup) { - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.CollectingItems()); - h.start(); - h.progress(Bundle.CollectingItems_Text()); - try { - compartments = getFlatCompartment(tenancy); - } finally { - h.finish(); - } - return this; - } - - @Override - public NotifyDescriptor createInput() { - if (onlyOneChoice()) { - throw new IllegalStateException("Input shouldn't be displayed for one choice"); // NOI18N - } - if (compartments.isEmpty()) { - return createQuickPick(compartments, Bundle.NoCompartment()); - } - return createQuickPick(compartments, Bundle.SelectCompartment()); - } - - @Override - public Step getNext() { - NextStepProvider nsProvider = lookup.lookup(NextStepProvider.class); - if (nsProvider != null) { - Step ns = nsProvider.nextStepFor(this); - if (ns != null) { - return ns.prepare(getValue(), lookup); + + private AbstractStep getNextFor(AbstractStep step) { + Steps.NextStepProvider nsProvider = lookup.lookup(Steps.NextStepProvider.class); + if (nsProvider != null) { + AbstractStep ns = nsProvider.nextStepFor(step); + if (ns != null) { + prepare(ns); + return ns; + } + } + return null; } - } - return null; - } - - @Override - public void setValue(String selected) { - this.selected = (CompartmentItem) compartments.get(selected); - } - - @Override - public CompartmentItem getValue() { - if (onlyOneChoice()) { - return (CompartmentItem) compartments.values().iterator().next(); - } - return selected; + }; } - @Override - public boolean onlyOneChoice() { - return compartments.size() == 1; - } - } - - /** - * Show list of items for a suggested type. - * - */ - static class SuggestedStep implements Step { - - private Map items = new HashMap<>(); - private OCIItem selected; - private Lookup lookup; - private final String suggestedType; - - public SuggestedStep(String suggestedType) { - this.suggestedType = suggestedType; + public Values getResult() { + return values; } - public SuggestedStep prepare(CompartmentItem compartment, Lookup lookup) { - this.lookup = lookup; - ProgressHandle h = ProgressHandle.createHandle(Bundle.CollectingItems()); - h.start(); - h.progress(Bundle.CollectingItems_Text()); - try { - getItemsByPath(compartment, suggestedType).forEach((db) -> items.put(db.getName(), db)); - } finally { - h.finish(); - } - return this; - } + private static class MultistepValues implements Values { + private final List steps; - private String getSuggestedItemName() { - switch (suggestedType) { - case "Databases": - return Bundle.Databases(); - case "Vault": - return Bundle.Vault(); - case "Bucket": - return Bundle.Bucket(); - case "Cluster": - return Bundle.Cluster(); - case "ComputeInstance": - return Bundle.Compute(); + public MultistepValues(List steps) { + this.steps = steps; } - throw new MissingResourceException("Missing OCI type", null, suggestedType); - } - - @Override - public NotifyDescriptor createInput() { - return createQuickPick(items, Bundle.SelectItem(getSuggestedItemName())); - } - - @Override - public Step getNext() { - NextStepProvider nsProvider = lookup.lookup(NextStepProvider.class); - if (nsProvider != null) { - Step ns = nsProvider.nextStepFor(this); - if (ns != null) { - return ns.prepare(getValue(), lookup); + + @Override + public T getValueForStep(Class> forStep) { + for (AbstractStep step : steps) { + if (step.getClass().equals(forStep)) { + return (T) step.getValue(); + } } - } - return null; - } - - @Override - public void setValue(String selected) { - this.selected = items.get(selected); - } - - @Override - public OCIItem getValue() { - if (onlyOneChoice()) { - selected = items.values().iterator().next(); + return null; } - return selected; - } - - @Override - public boolean onlyOneChoice() { - return false; } } - private static NotifyDescriptor.QuickPick createQuickPick(Map ociItems, String title) { + public static NotifyDescriptor.QuickPick createQuickPick(Map ociItems, String title) { List items = new ArrayList<> (); for (Map.Entry entry : ociItems.entrySet()) { String description = entry.getValue().getDescription(); @@ -470,282 +287,4 @@ private static NotifyDescriptor.QuickPick createQuickPick(Ma return new NotifyDescriptor.QuickPick(title, title, items, false); } - /** - * Retrieve all compartments from a tenancy. - * - * @param tenancy - * @return - */ - static private Map getFlatCompartment(TenancyItem tenancy) { - Map compartments = new HashMap<>(); - OCISessionInitiator session = OCIManager.getDefault().getActiveSession(); - Identity identityClient = session.newClient(IdentityClient.class); - String nextPageToken = null; - - do { - ListCompartmentsResponse response - = identityClient.listCompartments( - ListCompartmentsRequest.builder() - .compartmentId(tenancy.getKey().getValue()) - .compartmentIdInSubtree(true) - .lifecycleState(Compartment.LifecycleState.Active) - .accessLevel(ListCompartmentsRequest.AccessLevel.Accessible) - .limit(1000) - .page(nextPageToken) - .build()); - for (Compartment comp : response.getItems()) { - FlatCompartmentItem ci = new FlatCompartmentItem(comp) { - FlatCompartmentItem getItem(OCID compId) { - return compartments.get(compId); - } - }; - compartments.put(ci.getKey(), ci); - } - nextPageToken = response.getOpcNextPage(); - } while (nextPageToken != null); - Map pickItems = computeFlatNames(compartments); - pickItems.put(tenancy.getName() + " (root)", tenancy); // NOI18N - return pickItems; - } - - static private Map computeFlatNames(Map compartments) { - Map pickItems = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - for (FlatCompartmentItem comp : compartments.values()) { - pickItems.put(comp.getName(), comp); - } - return pickItems; - } - - /** - * This class represents compartments in a flat structure. Individual levels are separated by slashes. - * - */ - static private abstract class FlatCompartmentItem extends CompartmentItem { - - private final OCID parentId; - private String flatName; - - private FlatCompartmentItem(Compartment ociComp) { - super(OCID.of(ociComp.getId(), "Compartment"), ociComp.getCompartmentId(), ociComp.getName()); // NOI18N - setDescription(ociComp.getDescription()); - parentId = OCID.of(ociComp.getCompartmentId(), "Compartment"); // NOI18N - } - - public String getName() { - if (parentId.getValue() == null) { - return ""; - } - if (flatName == null) { - String parentFlatName = ""; - FlatCompartmentItem parentComp = getItem(parentId); - if (parentComp != null) { - parentFlatName = parentComp.getName(); - } - flatName = super.getName(); - if (!parentFlatName.isEmpty()) { - flatName = parentFlatName + "/" + flatName; // NOI18N - } - } - return flatName; - } - - abstract FlatCompartmentItem getItem(OCID compId); - } - - /** - * This step allows the user to select which type of resource will be added. - */ - static class ItemTypeStep implements Step { - String[] types = {"Databases", "Vault", "Bucket"}; //NOI18N - - private Lookup lookup; - private String selected; - - @Override - public Step prepare(Object item, Lookup lookup) { - this.lookup = lookup; - return this; - } - - @Override - public NotifyDescriptor createInput() { - List items = new ArrayList<>(types.length); - for (String itemType : types) { - items.add(new NotifyDescriptor.QuickPick.Item(itemType, itemType)); - } - return new NotifyDescriptor.QuickPick(Bundle.SelectResourceType(), Bundle.SelectResourceType(), items, false); - } - - @Override - public boolean onlyOneChoice() { - return false; - } - - @Override - public Step getNext() { - NextStepProvider nsProvider = NextStepProvider.builder() - .stepForClass(CompartmentStep.class, (s) -> new SuggestedStep(getValue())) - .stepForClass(SuggestedStep.class, (s) -> new ProjectStep()) - .build(); - return new TenancyStep().prepare(null, new ProxyLookup(Lookups.fixed(nsProvider), lookup)); - } - - @Override - public void setValue(String selected) { - this.selected = selected; - } - - @Override - public String getValue() { - return selected; - } - - } - - /** - * The purpose of this step is to select a project to update dependencies. - */ - public static class ProjectStep implements Step { - - private final CompletableFuture projectsFuture; - Map projects; - private Project selectedProject; - private Object item; - - public ProjectStep() { - projectsFuture = OpenProjectsFinder.getDefault().findTopLevelProjects(); - this.projects = new HashMap<> (); - } - - @Override - public Step prepare(Object item, Lookup lookup) { - this.item = item; - try { - Project[] p = projectsFuture.get(); - for (int i = 0; i < p.length; i++) { - ProjectInformation pi = ProjectUtils.getInformation(p[i]); - projects.put(pi.getDisplayName(), p[i]); - } - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } catch (ExecutionException ex) { - Exceptions.printStackTrace(ex); - } - return this; - } - - @Override - public NotifyDescriptor createInput() { - List items = new ArrayList<>(projects.size()); - for (Map.Entry entry : projects.entrySet()) { - - items.add(new NotifyDescriptor.QuickPick.Item( - entry.getKey(), - entry.getValue().getProjectDirectory().getName())); - } - String title = Bundle.SelectProject(); - if (projects.size() == 0) { - title = Bundle.NoProjects(); - } - return new NotifyDescriptor.QuickPick(title, title, items, false); - } - - @Override - public boolean onlyOneChoice() { - return projects.size() == 1; - } - - @Override - public Step getNext() { - return null; - } - - @Override - public void setValue(String selected) { - selectedProject = projects.get(selected); - } - - @Override - public Object getValue() { - if (projects.size() == 1) { - selectedProject = (Project) projects.values().toArray()[0]; - } - return Pair.of(selectedProject, item); - } - - } - - /** - * Retrieve items of a given type from a specified compartment. - * @param parent Compartment to search for items - * @param path Type of the items - * @return List of items found - */ - protected static List getItemsByPath(CompartmentItem parent, String path) { - Map items = new HashMap<>(); - try { - switch (path) { - case "Databases": //NOI18N - return DatabaseNode.getDatabases().apply(parent); - case "Vault": //NOI18N - return VaultNode.getVaults().apply(parent); - case "Bucket": //NOI18N - return BucketNode.getBuckets().apply(parent); - case "Cluster": //NOI18N - return ClusterNode.getClusters().apply(parent); - case "ComputeInstance": //NOI18N - return ComputeInstanceNode.getComputeInstances().apply(parent); - default: - return Collections.emptyList(); - } - } catch (BmcException e) { - LOG.log(Level.SEVERE, "Unable to load vault list", e); //NOI18N - } - return Collections.emptyList(); - } - - /** - * Step to select an existing database connection from the Database Explorer. - * - */ - static final class DatabaseConnectionStep implements Step { - - private final Map adbConnections; - private DatabaseItem selected; - - public DatabaseConnectionStep(Map adbConnections) { - this.adbConnections = adbConnections; - } - - @Override - public Step prepare(Object empty, Lookup lookup) { - return this; - } - - @Override - public NotifyDescriptor createInput() { - return createQuickPick(adbConnections, Bundle.SelectDBConnection()); - } - - @Override - public Step getNext() { - return null; - } - - @Override - public void setValue(String selected) { - this.selected = (DatabaseItem) adbConnections.get(selected); - } - - @Override - public DatabaseItem getValue() { - return selected; - } - - @Override - public boolean onlyOneChoice() { - return false; - } - } - } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SuggestedItem.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SuggestedItem.java index a166afd07ec1..e207a18e3efb 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SuggestedItem.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/SuggestedItem.java @@ -32,7 +32,7 @@ "SelectDatabases=Select Oracle Autonomous Database", "SelectVault=Select OCI Vault", "SelectBucket=Select Object Storage Bucket", - "SelectCluster=Select Oracle Container Engine", + "SelectCluster=Select Oracle Container Engine for Kubernetes", "SelectCompute=Select Compute Instance" }) public final class SuggestedItem extends OCIItem { @@ -51,7 +51,7 @@ public String getPath() { } public Set getExclusivePaths() { - return exclusivePaths; + return Collections.unmodifiableSet(exclusivePaths); } public static SuggestedItem forPath(String path) { diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/database/DatabaseNode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/database/DatabaseNode.java index d00c005feb54..611581a68a26 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/database/DatabaseNode.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/database/DatabaseNode.java @@ -84,7 +84,7 @@ public static ChildrenProvider getDatabases() { OCID.of(d.getId(), "Databases"), //NOI18N compartmentId.getKey().getValue(), d.getDbName(), - d.getConnectionUrls().getOrdsUrl()+SERVICE_CONSOLE_SUFFIX, + d.getConnectionUrls().getOrdsUrl() + SERVICE_CONSOLE_SUFFIX, getConnectionName(profiles)); StringBuilder sb = new StringBuilder(); sb.append(Bundle.LBL_WorkloadType(d.getDbWorkload().getValue())); diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/NamedReference.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/NamedReference.java new file mode 100644 index 000000000000..8d8d77198d23 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/NamedReference.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.items; + +/** + * References a resource that can be used in source code under a specified name, such as a datasource or an OCI vault. + * + * @author Jan Horvath + */ +public interface NamedReference { + + /** + * Set a reference name. + * + * @param referenceName + */ + public void setReferenceName(String referenceName); + + /** + * Returns a reference name. + * @return + */ + public String getReferenceName(); + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/OCIItem.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/OCIItem.java index 3e823455a03b..863a80b37ee3 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/OCIItem.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/items/OCIItem.java @@ -18,9 +18,9 @@ */ package org.netbeans.modules.cloud.oracle.items; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.util.Objects; -import javax.swing.event.ChangeListener; -import org.openide.util.ChangeSupport; /** * Represents Oracle Cloud Resource identified by Oracle Cloud Identifier (OCID) @@ -32,7 +32,7 @@ public abstract class OCIItem { final String name; final String compartmentId; String description; - transient ChangeSupport changeSupport; + final transient PropertyChangeSupport changeSupport; /** * Construct a new {@code OCIItem}. @@ -45,7 +45,7 @@ public OCIItem(OCID id, String compartmentId, String name) { this.id = id; this.name = name; this.compartmentId = compartmentId; - changeSupport = new ChangeSupport(this); + changeSupport = new PropertyChangeSupport(this); } public OCIItem() { @@ -101,29 +101,29 @@ public void setDescription(String description) { * Triggers node refresh. */ public void refresh() { - changeSupport.fireChange(); + changeSupport.firePropertyChange("children", 0, 1); } /** * Adds a ChangeListener to the listener list. * - * @param listener the ChangeListener to be added. + * @param listener the PropertyChangeListener to be added. */ - public void addChangeListener(ChangeListener listener) { - changeSupport.addChangeListener(listener); + public void addChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); } /** * Removes a ChangeListener from the listener list. * - * @param listener the ChangeListener to be removed. + * @param listener the PropertyChangeListener to be removed. */ - public void removeChangeListener(ChangeListener listener) { - changeSupport.removeChangeListener(listener); + public void removeChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); } public int maxInProject() { - return 0; + return 1; } @Override @@ -156,5 +156,8 @@ public boolean equals(Object obj) { return Objects.equals(this.id, other.id); } + public void fireRefNameChanged(String oldRefName, String referenceName) { + changeSupport.firePropertyChange("referenceName", oldRefName, referenceName); + } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/policy/PolicyGenerator.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/policy/PolicyGenerator.java index d7beb09c1731..2d521b63101a 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/policy/PolicyGenerator.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/policy/PolicyGenerator.java @@ -18,17 +18,18 @@ */ package org.netbeans.modules.cloud.oracle.policy; -import java.util.List; +import java.util.Collection; import org.netbeans.modules.cloud.oracle.assets.SuggestedItem; import org.netbeans.modules.cloud.oracle.items.OCIItem; /** - * + * Creates application.properties, bootstrap.properties and OCI Vault secrets from current contents of the {@link CloudAssets}. + * * @author Jan Horvath */ public class PolicyGenerator { - public static String createPolicies(List items) { + public static String createPolicies(Collection items) { OCIItem execution = null; String principalType; for (OCIItem item : items) { diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/CompartmentStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/CompartmentStep.java new file mode 100644 index 000000000000..e5a74d40e995 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/CompartmentStep.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import com.oracle.bmc.identity.Identity; +import com.oracle.bmc.identity.IdentityClient; +import com.oracle.bmc.identity.model.Compartment; +import com.oracle.bmc.identity.requests.ListCompartmentsRequest; +import com.oracle.bmc.identity.responses.ListCompartmentsResponse; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.OCIManager; +import org.netbeans.modules.cloud.oracle.OCISessionInitiator; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import static org.netbeans.modules.cloud.oracle.assets.Steps.createQuickPick; +import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; +import org.netbeans.modules.cloud.oracle.items.OCID; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.items.TenancyItem; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "NoCompartment=There are no compartments in the Tenancy", + "CollectingItems_Text=Listing compartments and databases", + "SelectCompartment=Select Compartment",}) +public final class CompartmentStep extends AbstractStep { + + private Map compartments = null; + private CompartmentItem selected; + + @Override + public void prepare(ProgressHandle h, Values values) { + h.progress(Bundle.CollectingItems_Text()); + TenancyItem tenancy = values.getValueForStep(TenancyStep.class); + compartments = getFlatCompartment(tenancy); + } + + @Override + public NotifyDescriptor createInput() { + if (onlyOneChoice()) { + throw new IllegalStateException("Input shouldn't be displayed for one choice"); // NOI18N + } + if (compartments.isEmpty()) { + return createQuickPick(compartments, Bundle.NoCompartment()); + } + return createQuickPick(compartments, Bundle.SelectCompartment()); + } + + @Override + public void setValue(String selected) { + this.selected = (CompartmentItem) compartments.get(selected); + } + + @Override + public CompartmentItem getValue() { + if (onlyOneChoice()) { + return (CompartmentItem) compartments.values().iterator().next(); + } + return selected; + } + + @Override + public boolean onlyOneChoice() { + return compartments.size() == 1; + } + + /** + * Retrieve all compartments from a tenancy. + * + * @param tenancy + * @return + */ + static private Map getFlatCompartment(TenancyItem tenancy) { + Map compartments = new HashMap<>(); + OCISessionInitiator session = OCIManager.getDefault().getActiveSession(); + Identity identityClient = session.newClient(IdentityClient.class); + String nextPageToken = null; + + do { + ListCompartmentsResponse response + = identityClient.listCompartments( + ListCompartmentsRequest.builder() + .compartmentId(tenancy.getKey().getValue()) + .compartmentIdInSubtree(true) + .lifecycleState(Compartment.LifecycleState.Active) + .accessLevel(ListCompartmentsRequest.AccessLevel.Accessible) + .limit(1000) + .page(nextPageToken) + .build()); + for (Compartment comp : response.getItems()) { + FlatCompartmentItem ci = new FlatCompartmentItem(comp) { + @Override + FlatCompartmentItem getItem(OCID compId) { + return compartments.get(compId); + } + }; + compartments.put(ci.getKey(), ci); + } + nextPageToken = response.getOpcNextPage(); + } while (nextPageToken != null); + Map pickItems = computeFlatNames(compartments); + pickItems.put(tenancy.getName() + " (root)", tenancy); // NOI18N + return pickItems; + } + + static private Map computeFlatNames(Map compartments) { + Map pickItems = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (FlatCompartmentItem comp : compartments.values()) { + pickItems.put(comp.getName(), comp); + } + return pickItems; + } + + /** + * This class represents compartments in a flat structure. Individual levels + * are separated by slashes. + * + */ + static private abstract class FlatCompartmentItem extends CompartmentItem { + + private final OCID parentId; + private String flatName; + + private FlatCompartmentItem(Compartment ociComp) { + super(OCID.of(ociComp.getId(), "Compartment"), ociComp.getCompartmentId(), ociComp.getName()); // NOI18N + setDescription(ociComp.getDescription()); + parentId = OCID.of(ociComp.getCompartmentId(), "Compartment"); // NOI18N + } + + public String getName() { + if (parentId.getValue() == null) { + return ""; + } + if (flatName == null) { + String parentFlatName = ""; + FlatCompartmentItem parentComp = getItem(parentId); + if (parentComp != null) { + parentFlatName = parentComp.getName(); + } + flatName = super.getName(); + if (!parentFlatName.isEmpty()) { + flatName = parentFlatName + "/" + flatName; // NOI18N + } + } + return flatName; + } + + abstract FlatCompartmentItem getItem(OCID compId); + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatabaseConnectionStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatabaseConnectionStep.java new file mode 100644 index 000000000000..75de3288c71c --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatabaseConnectionStep.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import java.util.HashMap; +import java.util.Map; +import org.netbeans.api.db.explorer.ConnectionManager; +import org.netbeans.api.db.explorer.DatabaseConnection; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.database.DatabaseItem; +import org.netbeans.modules.cloud.oracle.items.OCID; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * Step to select an existing database connection from the Database Explorer. + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "SelectDBConnection=Select Database Connection" +}) +public final class DatabaseConnectionStep extends AbstractStep { + + private final Map adbConnections; + private DatabaseItem selected = null; + + public DatabaseConnectionStep() { + adbConnections = new HashMap<>(); + DatabaseConnection[] connections = ConnectionManager.getDefault().getConnections(); + for (int i = 0; i < connections.length; i++) { + String name = connections[i].getDisplayName(); + String ocid = connections[i].getConnectionProperties().getProperty("OCID"); //NOI18N + String compartmentId = connections[i].getConnectionProperties().getProperty("CompartmentOCID"); //NOI18N + String description = connections[i].getConnectionProperties().getProperty("Description"); //NOI18N + if (ocid != null && compartmentId != null) { + DatabaseItem dbItem + = new DatabaseItem(OCID.of(ocid, "Databases"), compartmentId, name, null, name); //NOI18N + dbItem.setDescription(description); + adbConnections.put(name, dbItem); + } + } + } + + @Override + public NotifyDescriptor createInput() { + return Steps.createQuickPick(adbConnections, Bundle.SelectDBConnection()); + } + + @Override + public void setValue(String selected) { + this.selected = (DatabaseItem) adbConnections.get(selected); + } + + @Override + public DatabaseItem getValue() { + return selected; + } + + @Override + public boolean onlyOneChoice() { + return adbConnections.isEmpty(); + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatasourceNameStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatasourceNameStep.java new file mode 100644 index 000000000000..3775286901e7 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DatasourceNameStep.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "DatasourceName=Datasource Name", +}) +public class DatasourceNameStep extends AbstractStep { + private String selected = null; + + public DatasourceNameStep() { + } + + @Override + public void prepare(ProgressHandle h, Values values) { + } + + @Override + public NotifyDescriptor createInput() { + return new NotifyDescriptor.InputLine(selected == null ? "" : "DEFAULT", Bundle.DatasourceName()); //NOI18N + } + + @Override + public void setValue(String selected) { + this.selected = selected; + } + + @Override + public String getValue() { + return selected; + } + + @Override + public boolean onlyOneChoice() { + return false; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DevopsStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DevopsStep.java new file mode 100644 index 000000000000..63cfb7742f99 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/DevopsStep.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import com.oracle.bmc.devops.DevopsClient; +import com.oracle.bmc.devops.model.ProjectSummary; +import com.oracle.bmc.devops.requests.ListProjectsRequest; +import com.oracle.bmc.devops.responses.ListProjectsResponse; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.OCIManager; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; +import org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem; +import org.netbeans.modules.cloud.oracle.devops.DevopsProjectService; +import org.netbeans.modules.cloud.oracle.items.OCID; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "FetchingDevopsProjects=Fetching DevOps projects", + "NoDevopsProjects=There are no Devops Projects in selected Compartment", + "SelectDevopsProject=Select Devops Project" +}) +public class DevopsStep extends AbstractStep { + + private Map devopsProjects; + private DevopsProjectItem selected; + + @Override + public void prepare(ProgressHandle h, Values values) { + h.progress(Bundle.FetchingDevopsProjects()); + List devops = DevopsProjectService.getDevopsProjectOcid(); + CompartmentItem compartment = values.getValueForStep(CompartmentStep.class); + Map allProjectsInCompartment = getDevopsProjects(compartment.getKey().getValue()); + Map filtered = allProjectsInCompartment.entrySet().stream().filter(e -> devops.contains(e.getValue().getKey().getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + if (!filtered.isEmpty()) { + devopsProjects = filtered; + } else { + devopsProjects = allProjectsInCompartment; + } + if (devopsProjects.size() == 1) { + selected = devopsProjects.values().iterator().next(); + } + } + + @Override + public NotifyDescriptor createInput() { + if (devopsProjects.size() > 1) { + return Steps.createQuickPick(devopsProjects, Bundle.SelectDevopsProject()); + } + if (devopsProjects.isEmpty()) { + return new NotifyDescriptor.QuickPick("", Bundle.NoDevopsProjects(), Collections.emptyList(), false); + } + throw new IllegalStateException("No data to create input"); // NOI18N + } + + @Override + public boolean onlyOneChoice() { + return devopsProjects.size() == 1; + } + + @Override + public void setValue(String projectName) { + selected = devopsProjects.get(projectName); + } + + @Override + public DevopsProjectItem getValue() { + return selected; + } + + protected static Map getDevopsProjects(String compartmentId) { + try (DevopsClient client = new DevopsClient(OCIManager.getDefault().getConfigProvider())) { + ListProjectsRequest request = ListProjectsRequest.builder().compartmentId(compartmentId).build(); + ListProjectsResponse response = client.listProjects(request); + List projects = response.getProjectCollection().getItems(); + for (ProjectSummary project : projects) { + project.getNotificationConfig().getTopicId(); + } + return projects.stream().map(p -> new DevopsProjectItem(OCID.of(p.getId(), "DevopsProject"), // NOI18N + compartmentId, p.getName())).collect(Collectors.toMap(DevopsProjectItem::getName, Function.identity())); + } + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ItemTypeStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ItemTypeStep.java new file mode 100644 index 000000000000..7321728dc8c0 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ItemTypeStep.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * This step allows the user to select which type of resource will be added. + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "SelectResourceType=Select Resource Type" +}) +public class ItemTypeStep extends AbstractStep { + + private static final Map TYPES = new HashMap() { + { + put("Databases", Bundle.Databases()); //NOI18N + put("Bucket", Bundle.Bucket()); //NOI18N + put("Vault", Bundle.Vault()); //NOI18N + } + }; + private String selected; + + @Override + public void prepare(ProgressHandle h, Values values) { + } + + @Override + public NotifyDescriptor createInput() { + List items = new ArrayList<>(TYPES.size()); + for (Map.Entry itemType : TYPES.entrySet()) { + items.add(new NotifyDescriptor.QuickPick.Item(itemType.getKey(), itemType.getValue())); + } + return new NotifyDescriptor.QuickPick(Bundle.SelectResourceType(), Bundle.SelectResourceType(), items, false); + } + + @Override + public boolean onlyOneChoice() { + return false; + } + + @Override + public void setValue(String selected) { + this.selected = selected; + } + + @Override + public String getValue() { + return selected; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/KeyStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/KeyStep.java new file mode 100644 index 000000000000..9ca674e1bb9e --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/KeyStep.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import com.oracle.bmc.model.BmcException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.vault.KeyItem; +import org.netbeans.modules.cloud.oracle.vault.KeyNode; +import org.netbeans.modules.cloud.oracle.vault.VaultItem; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "NoKeys=No keys in this Vault. Select another one." +}) +public class KeyStep extends AbstractStep { + private static final Logger LOG = Logger.getLogger(KeyStep.class.getName()); + private Map keys = null; + private KeyItem selected; + private VaultItem vault; + + public KeyStep(VaultItem vault) { + this.vault = vault; + } + + public KeyStep() { + vault = null; + } + + @Override + public void prepare(ProgressHandle h, Values values) { + if (vault == null) { + vault = values.getValueForStep(VaultStep.class); + } + keys = getKeys(vault); + } + + @Override + public boolean onlyOneChoice() { + return keys.size() == 1; + } + + @Override + public NotifyDescriptor createInput() { + if (keys.size() > 1) { + return Steps.createQuickPick(keys, Bundle.SelectKey()); + } + if (keys.isEmpty()) { + return new NotifyDescriptor.QuickPick("", Bundle.NoKeys(), Collections.emptyList(), false); + } + throw new IllegalStateException("No data to create input"); // NOI18N + } + + @Override + public void setValue(String selected) { + this.selected = keys.get(selected); + } + + @Override + public KeyItem getValue() { + if (keys.size() == 1) { + return keys.values().iterator().next(); + } + return selected; + } + + protected Map getKeys(OCIItem parent) { + Map items = new HashMap<>(); + try { + if (parent instanceof VaultItem) { + KeyNode.getKeys().apply((VaultItem) parent).forEach(key -> items.put(key.getName(), key)); + } + } catch (BmcException e) { + LOG.log(Level.SEVERE, "Unable to load key list", e); //NOI18N + } + return items; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStep.java new file mode 100644 index 000000000000..bab2f4977818 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStep.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.vault.SecretItem; +import org.netbeans.modules.cloud.oracle.vault.SecretNode; +import org.netbeans.modules.cloud.oracle.vault.VaultItem; +import org.openide.NotifyDescriptor; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "SecretExists=Secrets with name {0} already exists", + "Cancel=Cancel", + "AddVersion=Add new versions", + "DatasourceEmpty=Datasource name cannot be empty" +}) +public class OverwriteStep extends AbstractStep { + + private Set dsNames; + private String choice; + private Lookup lookup; + private String dsName; + private static final Pattern p = Pattern.compile("[A-Z]*_([a-zA-Z0-9]*)_[A-Z]*"); //NOI18N + + @Override + public void prepare(ProgressHandle h, Values values) { + dsName = values.getValueForStep(DatasourceNameStep.class); + VaultItem vault = values.getValueForStep(VaultStep.class); + if (dsName == null || dsName.isEmpty() || vault == null) { + return; + } + List secrets = SecretNode.getSecrets().apply(vault); + this.dsNames = secrets.stream().map(s -> extractDatasourceName(s.getName())).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + @Override + public NotifyDescriptor createInput() { + if (dsName == null || dsName.isEmpty()) { + return new NotifyDescriptor.QuickPick("", Bundle.DatasourceEmpty(), Collections.emptyList(), false); + } + List yesNo = new ArrayList(); + yesNo.add(new NotifyDescriptor.QuickPick.Item(Bundle.AddVersion(), "")); + yesNo.add(new NotifyDescriptor.QuickPick.Item(Bundle.Cancel(), "")); + return new NotifyDescriptor.QuickPick("", Bundle.SecretExists(dsName), yesNo, false); + } + + @Override + public void setValue(String choice) { + this.choice = choice; + } + + @Override + public Boolean getValue() { + return Bundle.AddVersion().equals(choice) || onlyOneChoice(); + } + + @Override + public boolean onlyOneChoice() { + return dsNames == null || !dsNames.contains(dsName); + } + + protected static String extractDatasourceName(String value) { + Matcher m = p.matcher(value); + if (m.matches()) { + return m.group(1); + } + return null; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/PasswordStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/PasswordStep.java new file mode 100644 index 000000000000..2bac42b5a62b --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/PasswordStep.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author honza + */ +@NbBundle.Messages({ + "Password=Enter password for Database user {0}", +}) +public class PasswordStep extends AbstractStep { + + private boolean ask; + private String password; + private final String username; + + public PasswordStep(String password, String username) { + this.password = password; + this.username = username; + } + + @Override + public void prepare(ProgressHandle h, Values values) { + ask = password == null || password.isEmpty(); + } + + @Override + public NotifyDescriptor createInput() { + return new NotifyDescriptor.PasswordLine("DEFAULT", Bundle.Password(username)); //NOI18N + } + + @Override + public boolean onlyOneChoice() { + return !ask; + } + + @Override + public void setValue(String password) { + this.password = password; + } + + @Override + public String getValue() { + return password; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ProjectStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ProjectStep.java new file mode 100644 index 000000000000..5cd9ce751e95 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/ProjectStep.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.project.Project; +import org.netbeans.api.project.ProjectInformation; +import org.netbeans.api.project.ProjectUtils; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.OpenProjectsFinder; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.openide.NotifyDescriptor; +import org.openide.util.Exceptions; +import org.openide.util.NbBundle; + +/** + * The purpose of this step is to select a project to update dependencies. + */ +@NbBundle.Messages({ + "SelectProject=Select Project to Update Dependencies", + "NoProjects=No Project Found",}) +public class ProjectStep extends AbstractStep { + + private final CompletableFuture projectsFuture; + private final Map projects; + private Project selectedProject; + + public ProjectStep() { + projectsFuture = OpenProjectsFinder.getDefault().findTopLevelProjects(); + this.projects = new HashMap<>(); + } + + @Override + public void prepare(ProgressHandle h, Values values) { + try { + Project[] p = projectsFuture.get(); + for (int i = 0; i < p.length; i++) { + ProjectInformation pi = ProjectUtils.getInformation(p[i]); + projects.put(pi.getDisplayName(), p[i]); + } + } catch (InterruptedException | ExecutionException ex) { + Exceptions.printStackTrace(ex); + } + } + + @Override + public NotifyDescriptor createInput() { + List items = new ArrayList<>(projects.size()); + for (Map.Entry entry : projects.entrySet()) { + items.add(new NotifyDescriptor.QuickPick.Item(entry.getKey(), entry.getValue().getProjectDirectory().getName())); + } + String title = Bundle.SelectProject(); + if (projects.isEmpty()) { + title = Bundle.NoProjects(); + } + return new NotifyDescriptor.QuickPick(title, title, items, false); + } + + @Override + public boolean onlyOneChoice() { + return projects.size() == 1; + } + + @Override + public void setValue(String selected) { + selectedProject = projects.get(selected); + } + + @Override + public Project getValue() { + if (projects.size() == 1) { + return (Project) projects.values().toArray()[0]; + } + return selectedProject; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/SuggestedStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/SuggestedStep.java new file mode 100644 index 000000000000..5d8a29103ca0 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/SuggestedStep.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import com.oracle.bmc.model.BmcException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.bucket.BucketNode; +import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; +import org.netbeans.modules.cloud.oracle.compute.ClusterNode; +import org.netbeans.modules.cloud.oracle.compute.ComputeInstanceNode; +import org.netbeans.modules.cloud.oracle.database.DatabaseNode; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import org.netbeans.modules.cloud.oracle.vault.VaultNode; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * Show list of items for a suggested type. + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "Databases=Oracle Autonomous Database", + "Vault=OCI Vault", + "Bucket=Object Storage Bucket", + "Cluster=Oracle Container Engine", + "Compute=Compute Instance", + "SelectItem=Select {0}", +}) +public class SuggestedStep extends AbstractStep { + private static final Logger LOG = Logger.getLogger(SuggestedStep.class.getName()); + private final Map items = new HashMap<>(); + private OCIItem selected; + private String suggestedType; + + public SuggestedStep(String suggestedType) { + this.suggestedType = suggestedType; + } + + @Override + public void prepare(ProgressHandle h, Values values) { + h.progress(Bundle.CollectingItems_Text()); + if (suggestedType == null) { + suggestedType = values.getValueForStep(ItemTypeStep.class); + } + CompartmentItem compartment = values.getValueForStep(CompartmentStep.class); + getItemsByPath(compartment, suggestedType) + .forEach(db -> items.put(db.getName(), db)); + } + + private String getSuggestedItemName() { + switch (suggestedType) { + case "Databases": + return Bundle.Databases(); + case "Vault": + return Bundle.Vault(); + case "Bucket": + return Bundle.Bucket(); + case "Cluster": + return Bundle.Cluster(); + case "ComputeInstance": + return Bundle.Compute(); + } + throw new MissingResourceException("Missing OCI type", null, suggestedType); + } + + @Override + public NotifyDescriptor createInput() { + return Steps.createQuickPick(items, Bundle.SelectItem(getSuggestedItemName())); + } + + @Override + public void setValue(String selected) { + this.selected = items.get(selected); + } + + @Override + public OCIItem getValue() { + if (onlyOneChoice()) { + selected = items.values().iterator().next(); + } + return selected; + } + + @Override + public boolean onlyOneChoice() { + return false; + } + + /** + * Retrieve items of a given type from a specified compartment. + * @param parent Compartment to search for items + * @param path Type of the items + * @return List of items found + */ + protected static List getItemsByPath(CompartmentItem parent, String path) { + Map items = new HashMap<>(); + try { + switch (path) { + case "Databases": //NOI18N + return DatabaseNode.getDatabases().apply(parent); + case "Vault": //NOI18N + return VaultNode.getVaults().apply(parent); + case "Bucket": //NOI18N + return BucketNode.getBuckets().apply(parent); + case "Cluster": //NOI18N + return ClusterNode.getClusters().apply(parent); + case "ComputeInstance": //NOI18N + return ComputeInstanceNode.getComputeInstances().apply(parent); + default: + return Collections.emptyList(); + } + } catch (BmcException e) { + LOG.log(Level.SEVERE, "Unable to load vault list", e); //NOI18N + } + return Collections.emptyList(); + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/TenancyStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/TenancyStep.java new file mode 100644 index 000000000000..20964ee79319 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/TenancyStep.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import com.oracle.bmc.identity.model.Tenancy; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.OCIManager; +import org.netbeans.modules.cloud.oracle.OCIProfile; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.items.TenancyItem; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "SelectProfile=Select OCI Profile", + "SelectProfile_Description={0} (region: {1})", + "NoProfile=There is not any OCI profile in the config", + "CollectingProfiles_Text=Loading OCI Profiles", + "SelectKey=Select Key" +}) +public final class TenancyStep extends AbstractStep { + + private List profiles = new LinkedList<>(); + private final AtomicReference selected = new AtomicReference<>(); + + @Override + public NotifyDescriptor createInput() { + if (onlyOneChoice()) { + throw new IllegalStateException("No data to create input"); // NOI18N + } + String title = Bundle.SelectProfile(); + List items = new ArrayList<>(profiles.size()); + for (OCIProfile p : profiles) { + Tenancy t = p.getTenancyData(); + if (t != null) { + items.add(new NotifyDescriptor.QuickPick.Item(p.getId(), Bundle.SelectProfile_Description(t.getName(), t.getHomeRegionKey()))); + } + } + if (profiles.stream().filter(p -> p.getTenancy().isPresent()).count() == 0) { + title = Bundle.NoProfile(); + } + return new NotifyDescriptor.QuickPick(title, title, items, false); + } + + @Override + public void prepare(ProgressHandle h, Values values) { + h.progress(Bundle.CollectingProfiles_Text()); + profiles = OCIManager.getDefault().getConnectedProfiles(); + } + + @Override + public void setValue(String value) { + for (OCIProfile profile : profiles) { + if (profile.getId().equals(value)) { + profile.getTenancy().ifPresent(t -> this.selected.set(t)); + break; + } + } + } + + @Override + public TenancyItem getValue() { + if (onlyOneChoice()) { + return profiles.stream().map(p -> p.getTenancy()).filter(Optional::isPresent).map(Optional::get).findFirst().get(); + } + return selected.get(); + } + + @Override + public boolean onlyOneChoice() { + return profiles.stream().filter(p -> p.getTenancy().isPresent()).count() == 1; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/VaultStep.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/VaultStep.java new file mode 100644 index 000000000000..2cedd540cc05 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/steps/VaultStep.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.cloud.oracle.assets.AbstractStep; +import org.netbeans.modules.cloud.oracle.assets.Steps; +import org.netbeans.modules.cloud.oracle.assets.Steps.Values; +import org.netbeans.modules.cloud.oracle.compartment.CompartmentItem; +import org.netbeans.modules.cloud.oracle.vault.VaultItem; +import org.netbeans.modules.cloud.oracle.vault.VaultNode; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; + +/** + * + * @author Jan Horvath + */ +@NbBundle.Messages({ + "FetchingVaults=Fetching OCI Vaults", + "SelectVault=Select Vault", +}) +public class VaultStep extends AbstractStep { + private static final Logger LOG = Logger.getLogger(VaultStep.class.getName()); + private Map vaults = null; + private VaultItem selected; + + @Override + public void prepare(ProgressHandle h, Values values) { + h.progress(Bundle.FetchingVaults()); + CompartmentItem compartment = values.getValueForStep(CompartmentStep.class); + vaults = VaultNode.getVaults().apply((CompartmentItem) compartment).stream().collect(Collectors.toMap(VaultItem::getName, vault -> vault)); + } + + @Override + public NotifyDescriptor createInput() { + return Steps.createQuickPick(vaults, Bundle.SelectVault()); + } + + @Override + public void setValue(String selected) { + this.selected = vaults.get(selected); + } + + @Override + public VaultItem getValue() { + if (onlyOneChoice()) { + selected = vaults.values().iterator().next(); + } + return selected; + } + + @Override + public boolean onlyOneChoice() { + return vaults.size() == 1; + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/KeyNode.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/KeyNode.java index ca7f5f390bc5..812ffda7e43f 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/KeyNode.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/KeyNode.java @@ -25,7 +25,6 @@ import java.util.stream.Collectors; import org.netbeans.modules.cloud.oracle.ChildrenProvider; import org.netbeans.modules.cloud.oracle.NodeProvider; -import org.netbeans.modules.cloud.oracle.OCIManager; import org.netbeans.modules.cloud.oracle.OCINode; import org.netbeans.modules.cloud.oracle.items.OCID; import org.openide.nodes.Children; @@ -63,7 +62,7 @@ public static ChildrenProvider.SessionAware getKeys() { Vault v = Vault.builder() .compartmentId(vault.getCompartmentId()) .id(vault.getKey().getValue()) - .managementEndpoint(vault.managementEndpoint) + .managementEndpoint(vault.getManagementEndpoint()) .build(); KmsManagementClient client = KmsManagementClient.builder() .vault(v) diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/VaultItem.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/VaultItem.java index ca3e0b729541..d6c8da3d22c1 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/VaultItem.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/vault/VaultItem.java @@ -26,7 +26,7 @@ * @author Jan Horvath */ public class VaultItem extends OCIItem { - String managementEndpoint; + private String managementEndpoint; public VaultItem(OCID id, String compartment, String name, String managementEndpoint) { super(id, compartment, name); @@ -43,7 +43,7 @@ public String getManagementEndpoint() { @Override public int maxInProject() { - return Integer.MAX_VALUE; + return 1; } } diff --git a/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java index 9f79e00d2db0..9a9782d2aa7c 100644 --- a/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java +++ b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/actions/AddDbConnectionToVaultTest.java @@ -50,20 +50,6 @@ public void setUp() { public void tearDown() { } - /** - * Test of extractDatasourceName method, of class AddDbConnectionToVault. - */ - @Test - public void datasourceName() { - String input = "DATASOURCES_DEFAULT_USERNAME"; - String ds = AddDbConnectionToVault.extractDatasourceName(input); - assertEquals("DEFAULT", ds); - - input = "DATASOURCES_DEF3_USERNAME"; - ds = AddDbConnectionToVault.extractDatasourceName(input); - assertEquals("DEF3", ds); - } - @Test public void testConfigMap() { String cm = "apiVersion: v1\n" + diff --git a/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStepTest.java b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStepTest.java new file mode 100644 index 000000000000..440d34427c16 --- /dev/null +++ b/enterprise/cloud.oracle/test/unit/src/org/netbeans/modules/cloud/oracle/steps/OverwriteStepTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.cloud.oracle.steps; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Jan Horvath + */ +public class OverwriteStepTest { + + /** + * Test of extractDatasourceName method, of class AddDbConnectionToVault. + */ + @Test + public void datasourceName() { + String input = "DATASOURCES_DEFAULT_USERNAME"; + String ds = OverwriteStep.extractDatasourceName(input); + assertEquals("DEFAULT", ds); + + input = "DATASOURCES_DEF3_USERNAME"; + ds = OverwriteStep.extractDatasourceName(input); + assertEquals("DEF3", ds); + } + +} diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java new file mode 100644 index 000000000000..576442223e6d --- /dev/null +++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/LspAssetsDecorationProvider.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.netbeans.modules.nbcode.integration; + +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.modules.cloud.oracle.assets.CloudAssets; +import org.netbeans.modules.cloud.oracle.bucket.BucketItem; +import org.netbeans.modules.cloud.oracle.database.DatabaseItem; +import org.netbeans.modules.cloud.oracle.items.OCIItem; +import static org.netbeans.modules.java.lsp.server.explorer.DefaultDecorationsImpl.COOKIES_EXT; +import org.netbeans.modules.java.lsp.server.explorer.NodeLookupContextValues; +import org.netbeans.modules.java.lsp.server.explorer.api.TreeDataListener; +import org.netbeans.modules.java.lsp.server.explorer.api.TreeDataProvider; +import org.netbeans.modules.java.lsp.server.explorer.api.TreeItemData; +import org.openide.filesystems.FileObject; +import org.openide.nodes.Node; +import org.openide.util.lookup.ServiceProvider; + + +/** + * + * @author Jan Horvath + */ +@ServiceProvider(service = TreeDataProvider.Factory.class, path = "Explorers/cloud.assets") +public class LspAssetsDecorationProvider implements TreeDataProvider.Factory { + private static final Logger LOG = Logger.getLogger(LspAssetsDecorationProvider.class.getName()); + + public static final String CTXVALUE_CAP_REFERENCE_NAME = "cap:refName"; // NOI18N + public static final String CTXVALUE_PREFIX_REFERENCE_NAME = "cloudAssetsReferenceName:"; // NOI18N + + void readFiles(FileObject parent, List lines) { + if (parent == null) { + return; + } + for (FileObject f : parent.getChildren()) { + if (f.isData() && COOKIES_EXT.equals(f.getExt())) { + try { + f.asLines().stream().filter(s -> !s.trim().isEmpty() && !s.startsWith("#")).forEach(lines::add); // NOI18N + } catch (IOException ex) { + LOG.log(Level.WARNING, "Unable to read lookup items from {0}", f); + } + } + } + } + + @Override + public synchronized TreeDataProvider createProvider(String treeId) { + return new ProviderImpl(null); + } + + static class ProviderImpl implements TreeDataProvider { + public ProviderImpl(NodeLookupContextValues lookupValues) { + } + + @Override + public TreeItemData createDecorations(Node n, boolean expanded) { + TreeItemData d = new TreeItemData(); + String refName = null; + boolean set = false; + + OCIItem item = n.getLookup().lookup(OCIItem.class); + if (item != null) { + refName = CloudAssets.getDefault().getReferenceName(item); + } + if (refName != null) { + d.addContextValues(CTXVALUE_PREFIX_REFERENCE_NAME + refName); + set = true; + } + if (item instanceof BucketItem + || item instanceof DatabaseItem) { + d.addContextValues(CTXVALUE_CAP_REFERENCE_NAME); + set = true; + } + return set ? d : null; + } + + @Override + public void addTreeItemDataListener(TreeDataListener l) { + } + + @Override + public void removeTreeItemDataListener(TreeDataListener l) { + } + + @Override + public void nodeReleased(Node n) { + } + + } +} diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues index 4e9ec0636ecf..bf496b1768aa 100644 --- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues +++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/cloud-cookies.contextValues @@ -23,4 +23,6 @@ org.netbeans.modules.cloud.oracle.devops.BuildPipelineItem org.netbeans.modules.cloud.oracle.devops.DevopsProjectItem org.netbeans.modules.cloud.oracle.assets.SuggestedItem org.netbeans.modules.cloud.oracle.compute.ClusterItem -org.netbeans.modules.cloud.oracle.compute.ComputeInstance \ No newline at end of file +org.netbeans.modules.cloud.oracle.compute.ComputeInstanceItem +org.netbeans.modules.cloud.oracle.bucket.BucketItem +org.netbeans.modules.cloud.oracle.vault.VaultItem \ No newline at end of file diff --git a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml index a3415479a698..bb577a6a8b10 100644 --- a/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml +++ b/java/java.lsp.server/nbcode/integration/src/org/netbeans/modules/nbcode/integration/layer.xml @@ -61,6 +61,7 @@ + diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/cloud/OCIAssets.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/cloud/OCIAssets.java index d1cc2eb19856..914860033b6c 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/cloud/OCIAssets.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/cloud/OCIAssets.java @@ -36,7 +36,6 @@ public class OCIAssets implements ExplorerManagerFactory { public static final String ID = "cloud.assets"; // NOI18N public static final String CLOUD_PATH = "CloudAssets"; // NOI18N - private static String CLOUD_NODE_NAME = "cloudAssets"; @Override public CompletionStage createManager(String id, Lookup context) { @@ -46,7 +45,6 @@ public CompletionStage createManager(String id, Lookup context) ExplorerManager em = new ExplorerManager(); Lookup.Result nodes = Lookups.forPath("Cloud/Oracle/Assets").lookupResult(Node.class); nodes.allInstances().stream() -// .filter(n -> CLOUD_NODE_NAME.equals(n.getName())) .findFirst() .ifPresent(n -> em.setRootContext(n)); return CompletableFuture.completedFuture(em); diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/DefaultDecorationsImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/DefaultDecorationsImpl.java index 498eefd055d4..c99b758a035e 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/DefaultDecorationsImpl.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/DefaultDecorationsImpl.java @@ -216,7 +216,7 @@ public TreeItemData createDecorations(Node n, boolean expanded) { d.addContextValues(CTXVALUE_CAP_RENAME); set = true; } - + return set ? d : null; } diff --git a/java/java.lsp.server/vscode/package-lock.json b/java/java.lsp.server/vscode/package-lock.json index ef30fce4948e..e2f2b8b89997 100644 --- a/java/java.lsp.server/vscode/package-lock.json +++ b/java/java.lsp.server/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "apache-netbeans-java", - "version": "0.1.0", + "version": "99.99.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "apache-netbeans-java", - "version": "0.1.0", + "version": "99.99.9", "license": "Apache 2.0", "dependencies": { "@vscode/debugadapter": "1.55.1", diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json index 0afa8561e436..625521ed55c4 100644 --- a/java/java.lsp.server/vscode/package.json +++ b/java/java.lsp.server/vscode/package.json @@ -4,7 +4,7 @@ "description": "Apache NetBeans Language Server Extension for Visual Studio Code", "author": "Apache NetBeans", "license": "Apache 2.0", - "version": "0.1.0", + "version": "99.99.9", "preview": false, "repository": { "type": "git", @@ -78,7 +78,7 @@ }, { "id": "cloud.assets", - "name": "Cloud Assets", + "name": "Oracle Cloud Assets", "when": "nbJavaLSReady" }, { @@ -548,22 +548,27 @@ }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.AddToProject", - "title": "Add To Project", + "title": "Add to Oracle Cloud Assets", "icon": "$(add)" }, + { + "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.SetReferenceName", + "title": "Set a Reference Name", + "icon": "$(edit)" + }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.RemoveFromProject", - "title": "Remove From Project", + "title": "Remove from Oracle Cloud Assets", "icon": "$(remove)" }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.AddSuggestedItemAction", - "title": "Add To Project", + "title": "Add Suggested to Oracle Cloud Assets", "icon": "$(add)" }, { "command": "nbls.cloud.assets.refresh", - "title": "Refresh Cloud Assets", + "title": "Refresh Oracle Cloud Assets", "icon": "$(refresh)" }, { @@ -581,6 +586,11 @@ "title": "Add New Cloud Resource", "icon": "$(add)" }, + { + "command": "nbls.cloud.assets.configmap.upload", + "title": "Add Cloud Assets to ConfigMap", + "icon": "$(add)" + }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.CloudRefresh", "title": "Refresh", @@ -787,6 +797,10 @@ "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.AddToProject", "when": "false" }, + { + "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.SetReferenceName", + "when": "false" + }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.RemoveFromProject", "when": "false" @@ -975,7 +989,7 @@ }, { "command": "nbls.cloud.ocid.copy", - "when": "viewItem =~ /class:oracle.items.OCIItem/", + "when": "viewItem =~ /class:oracle.items.OCIItem/ && viewItem =~ /^(?!.*Suggested).*/", "group": "oci@1000" }, { @@ -990,7 +1004,12 @@ }, { "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.AddToProject", - "when": "viewItem =~ /class:oracle/ && view != cloud.assets", + "when": "viewItem =~ /(ComputeInstanceItem|ClusterItem|BucketItem|DatabaseItem|VaultItem)/ && view != cloud.assets", + "group": "inline@14" + }, + { + "command": "nbls:Tools:org.netbeans.modules.cloud.oracle.actions.SetReferenceName", + "when": "viewItem =~ /cap:refName/ && view == cloud.assets", "group": "inline@14" }, { diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index 0434ecc07400..823d054e37d4 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -347,6 +347,19 @@ function shouldEnableConflictingJavaSupport() : boolean | undefined { return r; } +function getValueAfterPrefix(input: string | undefined, prefix: string): string { + if (input === undefined) { + return ""; + } + const parts = input.split(' '); + for (let i = 0; i < parts.length; i++) { + if (parts[i].startsWith(prefix)) { + return parts[i].substring(prefix.length); + } + } + return ''; +} + export function activate(context: ExtensionContext): VSNetBeansAPI { const provider = new StringContentProvider(); const scheme = 'in-memory'; @@ -1282,9 +1295,24 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex if (enableJava) { c.findTreeViewService().createView('cloud.resources', undefined, { canSelectMany : false }); } - c.findTreeViewService().createView('cloud.assets', undefined, { canSelectMany : false, showCollapseAll: false }); + c.findTreeViewService().createView('cloud.assets', undefined, { canSelectMany : false, showCollapseAll: false , providerInitializer : (customizable) => + customizable.addItemDecorator(new CloudAssetsDecorator())}); }).catch(setClient[1]); + class CloudAssetsDecorator implements TreeItemDecorator { + decorateChildren(element: Visualizer, children: Visualizer[]): Visualizer[] { + return children; + } + + async decorateTreeItem(vis : Visualizer, item : vscode.TreeItem) : Promise { + item.description = getValueAfterPrefix(item.contextValue, "cloudAssetsReferenceName:"); + return item; + } + + dispose() { + } + } + class Decorator implements TreeItemDecorator { private provider : CustomizableTreeDataProvider; private setCommand : vscode.Disposable;