Skip to content

Conversation

@zhangzhx
Copy link
Contributor

First batch for AWS Explorer in IntelliJ.

  • Redesigned the Explorer with the IntelliJ tree framework in the package com.intellij.ide.util.treeView
  • Added two extra status node for showing errors and empty.

Empty Node
emptynode
Error Node
errornode

@zhangzhx zhangzhx requested review from abrooksv and kiiadi July 31, 2017 17:18
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the .iml file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is not necessary for importing the project, we can remove this. I added .iml files in .gitignore.

import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;

import javax.annotation.Nonnull;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use jet-brains annotations for this instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

*/
public class AwsRegionPanel {
private JPanel regionPanel;
private com.intellij.openapi.ui.ComboBox<AwsRegion> regionCombo;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is the FQ class name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imported the class and removed the FQ class name

}
});

for (AwsRegion region : AwsRegionManager.INSTANCE.getRegions()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use forEach instead ? 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

private void selectRegion(String regionId) {
for (AwsRegion region : AwsRegionManager.INSTANCE.getRegions()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stream().filter(r -> r.getId().equals(regionId)).findFirst().ifPresent(r -> regionCombo.setSelectedItem(r));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

class AwsExplorerLambdaRootNode(project: Project?, region: String):
AwsExplorerServiceRootNode<FunctionConfiguration>(project, "AWS Lambda", region, LAMBDA_SERVICE_ICON) {

private var client: AWSLambda = AWSLambdaClientBuilder.standard()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be a val ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original concern is when region gets updated so we need to create a new client. Our current implementation is to redraw the Tree when region gets updated to avoid potential bugs for updating the Tree. Therefore, I can change this to val.

class AwsExplorerRootNode(project: Project?, region: String):
AwsExplorerNode<String>(project, "ROOT", region, AWS_ICON) {

override fun getChildren(): MutableCollection<out AbstractTreeNode<String>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be a MutableCollection ? Can we make it immutable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/**
* Created by zhaoxiz on 7/27/17.
*/
enum class AwsExplorerService(val serviceId: String) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have this more like an extension point like @abrooksv did with the Credential Management? Then every time we add a new service (that has it's own 'resources' to display in the explorer) it can register as a provider here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my first version of the Explorer but dropped that after talking to @abrooksv. The reason was that:

  1. The Extension Point has a low performance.
  2. We are not planning to provide different IntelliJ plugins based on services like Eclipse do. We want to integrate all the services in one plugin. We provide different plugins based on different languages.

},
;

abstract fun buildServiceRoodNode(project: Project?, region: String): AbstractTreeNode<String>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rood -> root

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops, corrected.

}

// This method may throw RuntimeException, must handle it
abstract fun loadResources(): MutableList<Resource>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this throw an exception? Is there a better way of communicating this? What if it returned a nullable MutableList? Alternatively you can do this with an Either if the error is actually important

https://discuss.kotlinlang.org/t/disjoint-unions-in-kotlin/413

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loadResources() method mostly would throw an AmazonServiceException which is a RuntimeException.
The return type is MutableList<Resource>, so no null value is returned.
I think whether to use Either or not depends where do we want to handle this exception. I personally prefer to current way as the loadResources method should only be called in the super class when loading specific resources like S3 buckets or Lambda functions. The super class would handle the exceptions to report in the UI.

Copy link
Contributor

@abrooksv abrooksv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I would look into if we can convert more of the mutable stuff to immutable.

Also remove all the author blocks

/**
* Created by zhaoxiz on 7/20/17.
*/
public class ExplorerToolbar {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rename this since it is not just the toolbar, but the tree too. If its the full tool window then call it ExplorerToolWindow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. ExplorerToolWindow better fits the class name.

public ExplorerToolbar(@NonNls Project project, @NonNls Wrapper wrapper) {
this.project = project;
this.regionProvider = AwsDefaultRegionProvider.getInstance(project);
this.wrapper = wrapper;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the wrapper get into the mainPanel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mainPanel is only for the toolbar. The wrapper panel is for the tree. The wrapper panel is created in the class AwsExplorerFactory, the wrapper is passed in as a parameter so that when region changes, we can update the tree panel in the wrapper.

val simpleToolWindowPanel = SimpleToolWindowPanel(true, false)
val wrapperPane = Wrapper()

simpleToolWindowPanel.setToolbar(ExplorerToolWindow(project, wrapperPane).mainPanel)
simpleToolWindowPanel.setContent(wrapperPane)

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.amazonaws.intellij.ui.explorer.ExplorerToolbar">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not seeing much reason to bind this to a form since it is an empty panel. Do you see much reason to keep this as a bound .form instead of just writing it in Kotlin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this form.

private ComboBox<AwsRegion> regionCombo;

public AwsRegionPanel(String defaultRegion) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete blank line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
});

AwsRegionManager.INSTANCE.getRegions().forEach((region)-> {regionCombo.addItem(region);});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look into setting the ComboBox to use a CollectionComboBoxModel

This will remove the need for the stream, and also using the model is typesafe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

class AwsExplorerLambdaRootNode(project: Project?, region: String):
AwsExplorerServiceRootNode<FunctionConfiguration>(project, "AWS Lambda", region, LAMBDA_SERVICE_ICON) {

private val client: AWSLambda = AWSLambdaClientBuilder.standard()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add TODO to move to a client manager so we don't lose it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Will send a separate PR for ClientManager

.withRegion(region)
.build()

override fun loadResources(): MutableList<FunctionConfiguration> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not be mutable list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val simpleToolWindowPanel = SimpleToolWindowPanel(true, false)
val wrapperPane = Wrapper()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapper out here seems weird inversion of control. Can we move it into the tool panel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* Created by zhaoxiz on 7/27/17.
*/

abstract class AwsExplorerNode<T>(project: Project?, value: T, val region: String, val awsIcon: Icon?):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there an interface T is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an interface T? What do you mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opps thought I deleted this comment because I saw there wasn't

Question was is there better type bounds for T, basically

val cache: ClearableLazyValue<MutableCollection<AwsExplorerNode<*>>>

init {
cache = object : ClearableLazyValue<MutableCollection<AwsExplorerNode<*>>>() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to be mutable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with Immutable.

AwsRegionManager.INSTANCE.getRegions().forEach((region)-> {regionCombo.addItem(region);});
regionCombo.setModel(regionModel);

AwsRegionManager.INSTANCE.getRegions().forEach(regionModel::add);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just say regionModel.add(AwsRegionManager.INSTANCE.getRegions())

or in the constructor of the collection pass it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


private class AwsResources(val region: String) {
val s3Client: AmazonS3 = AmazonS3Client() //AmazonS3ClientBuilder.standard().withRegion(region).build()
val s3Client: AmazonS3 = AmazonS3Client() //AmazonS3ClientBuilder.standard().withRegion(region).parse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the commented out part for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to address this. This is because a rename to the method build() -> parse(), but somehow IntelliJ replaces all the occurrences of build to parse. This code would be replaced anyway, so leave it as is for now

private ComboBox<AwsRegion> regionCombo;
private final CollectionComboBoxModel<AwsRegion> regionModel = new CollectionComboBoxModel<>();

public AwsRegionPanel(String defaultRegion) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just take the AwsRegion type as the parameter? Saves us the filter and find later

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


public AwsRegionPanel(String defaultRegion) {

regionCombo.setRenderer(new DefaultListCellRenderer() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets look into ListCellRendererWrapper. It is type safe and the comment says it is less ugly 😸

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the info :)

object AwsRegionManager {

private val regions = mutableListOf<AwsRegion>()
var regions: List<AwsRegion>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be val

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel

class ExplorerToolWindow(val project: Project, val treePanelWrapper: Wrapper) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this extend the simple tool window and call setContent in init{}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Removed the form file also

Copy link
Contributor

@abrooksv abrooksv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just change the XML node name in the persistent component

@zhangzhx zhangzhx merged commit 76d39a1 into credManagement-for-explorer Aug 17, 2017
@abrooksv abrooksv deleted the aws-explorer branch August 17, 2017 01:09
hunterwerlla pushed a commit that referenced this pull request Oct 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants