-
Notifications
You must be signed in to change notification settings - Fork 275
AWS Explorer #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AWS Explorer #4
Changes from all commits
fa8f0f9
f4bc62c
9105362
3b3ca20
c756742
2ee671c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,5 +2,7 @@ | |
| out/ | ||
| target/ | ||
| /.idea/ | ||
| build/ | ||
| *.iml | ||
| .DS_Store | ||
| Build/ | ||
| Build/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.amazonaws.intellij.core.region | ||
|
|
||
| import com.intellij.openapi.components.PersistentStateComponent | ||
| import com.intellij.openapi.components.ServiceManager | ||
| import com.intellij.openapi.components.State | ||
| import com.intellij.openapi.components.Storage | ||
| import com.intellij.openapi.project.Project | ||
|
|
||
| @State(name = "region", storages = arrayOf(Storage("aws.xml"))) | ||
| class AwsDefaultRegionProvider(): | ||
| PersistentStateComponent<AwsDefaultRegionProvider.RegionState> { | ||
|
|
||
| data class RegionState(var currentRegion: String? = AwsRegionManager.defaultRegion.id) | ||
| private var regionState: RegionState = RegionState() | ||
| var currentRegion: AwsRegion | ||
| get() = AwsRegionManager.lookupRegionById(regionState.currentRegion?: AwsRegionManager.defaultRegion.id) | ||
| set(value) { regionState.currentRegion = value.id } | ||
|
|
||
| override fun loadState(regionState: RegionState) { | ||
| this.regionState.currentRegion = regionState.currentRegion | ||
| } | ||
|
|
||
| override fun getState(): RegionState { | ||
| return regionState | ||
| } | ||
|
|
||
| companion object { | ||
| @JvmStatic | ||
| fun getInstance(project: Project): AwsDefaultRegionProvider { | ||
| return ServiceManager.getService(project, AwsDefaultRegionProvider::class.java) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package com.amazonaws.intellij.core.region | ||
|
|
||
| import com.amazonaws.regions.Region | ||
| import com.amazonaws.regions.RegionUtils | ||
| import com.amazonaws.regions.Regions | ||
| import com.intellij.openapi.util.IconLoader | ||
| import javax.swing.Icon | ||
|
|
||
| data class AwsRegion private constructor(val id: String, val name: String, val icon: Icon) { | ||
| private companion object { | ||
| val UNKNOWN_REGION_FLAG = "/icons/aws-box.gif" | ||
| val REGION_FLAG_MAPPING = mapOf( | ||
| "us-east-1" to "/icons/flags/us.png", | ||
| "us-east-2" to "/icons/flags/us.png", | ||
| "us-west-1" to "/icons/flags/us.png", | ||
| "us-west-2" to "/icons/flags/us.png", | ||
| "ap-northeast-1" to "/icons/flags/japan.png", | ||
| "ap-southeast-1" to "/icons/flags/singapore.png", | ||
| "ap-southeast-2" to "/icons/flags/australia.png", | ||
| "eu-west-1" to "/icons/flags/ireland.png", | ||
| "eu-central-1" to "/icons/flags/eu.png", | ||
| "eu-west-2" to "/icons/flags/eu.png" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do. We are missing some more flags, and the service icons as well. Will add these all together.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we also make this immutable using mapOf
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| ) | ||
| } | ||
|
|
||
| constructor(id: String, name: String): | ||
| this(id, name, IconLoader.getIcon (REGION_FLAG_MAPPING.getOrDefault(id, UNKNOWN_REGION_FLAG))) | ||
|
|
||
| override fun toString(): String { | ||
| return name | ||
| } | ||
|
|
||
| fun isServiceSupported(serviceId: String): Boolean { | ||
| return RegionUtils.getRegion(id).isServiceSupported(serviceId) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package com.amazonaws.intellij.core.region | ||
|
|
||
| import com.amazonaws.intellij.utils.notifyException | ||
| import com.amazonaws.partitions.model.Partitions | ||
| import com.amazonaws.regions.RegionUtils | ||
| import com.fasterxml.jackson.core.JsonParser | ||
| import com.fasterxml.jackson.databind.DeserializationFeature | ||
| import com.fasterxml.jackson.databind.MapperFeature | ||
| import com.fasterxml.jackson.databind.ObjectMapper | ||
| import com.google.common.collect.ImmutableMap | ||
| import com.intellij.openapi.diagnostic.Logger | ||
| import java.io.IOException | ||
|
|
||
| object AwsRegionManager { | ||
| private const val DEFAULT_REGION = "us-west-2" | ||
| val regions: Map<String, AwsRegion> | ||
| val defaultRegion: AwsRegion | ||
|
|
||
| init { | ||
| val partitions = PartitionLoader.parse() | ||
|
|
||
| val mutableRegionMap = mutableMapOf<String, AwsRegion>() | ||
| partitions?.partitions?.forEach { | ||
| it.regions?.forEach { key, region -> mutableRegionMap.put(key, AwsRegion(key, region.description))} | ||
| } | ||
|
|
||
| regions = ImmutableMap.copyOf(mutableRegionMap) | ||
| //TODO Is there a better way to notify the customer and report the error to us instead of just crash? | ||
| defaultRegion = regions.get(DEFAULT_REGION)!! | ||
| } | ||
|
|
||
| fun lookupRegionById(regionId: String): AwsRegion { | ||
| return regions[regionId]?: defaultRegion | ||
| } | ||
|
|
||
| fun isServiceSupported(region: String, serviceName: String): Boolean { | ||
| return RegionUtils.getRegion(region).isServiceSupported(serviceName) | ||
| } | ||
| } | ||
|
|
||
| private object PartitionLoader { | ||
| //TODO This endpoint file should be update-to-date file | ||
| private const val JAVA_SDK_PARTITION_RESOURCE_PATH = "com/amazonaws/partitions/endpoints.json" | ||
| private val LOG = Logger.getInstance(PartitionLoader::class.java) | ||
|
|
||
| private val mapper = ObjectMapper() | ||
| .disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS) | ||
| .disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS) | ||
| .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | ||
| .enable(JsonParser.Feature.ALLOW_COMMENTS) | ||
|
|
||
| fun parse(): Partitions? { | ||
| PartitionLoader::class.java.classLoader.getResourceAsStream(JAVA_SDK_PARTITION_RESOURCE_PATH).use { | ||
| return try { | ||
| mapper.readValue<Partitions>(it, Partitions::class.java) | ||
| } catch (e: IOException) { | ||
| LOG.error("Error: failed to load file from $JAVA_SDK_PARTITION_RESOURCE_PATH !", e) | ||
| notifyException("Failed to load region endpoint file", e) | ||
| null | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.amazonaws.intellij.lambda.explorer | ||
|
|
||
| import com.amazonaws.intellij.ui.LAMBDA_SERVICE_ICON | ||
| import com.amazonaws.intellij.ui.SQS_QUEUE_ICON | ||
| import com.amazonaws.intellij.ui.explorer.AwsExplorerNode | ||
| import com.amazonaws.intellij.ui.explorer.AwsExplorerServiceRootNode | ||
| import com.amazonaws.services.lambda.AWSLambda | ||
| import com.amazonaws.services.lambda.AWSLambdaClientBuilder | ||
| import com.amazonaws.services.lambda.model.FunctionConfiguration | ||
| import com.intellij.ide.util.treeView.AbstractTreeNode | ||
| import com.intellij.openapi.project.Project | ||
|
|
||
| class AwsExplorerLambdaRootNode(project: Project, region: String): | ||
| AwsExplorerServiceRootNode<FunctionConfiguration>(project, "AWS Lambda", region, LAMBDA_SERVICE_ICON) { | ||
|
|
||
| //TODO we need to move to ClientFactory for initializing service client | ||
| private val client: AWSLambda = AWSLambdaClientBuilder.standard() | ||
| .withRegion(region) | ||
| .build() | ||
|
|
||
| override fun loadResources(): Collection<FunctionConfiguration> { | ||
| //TODO We need to list all the functions, not just one page | ||
| return client.listFunctions().functions | ||
| } | ||
|
|
||
| override fun mapResourceToNode(resource: FunctionConfiguration) = AwsExplorerFunctionNode(project!!, resource, region) | ||
| } | ||
|
|
||
| class AwsExplorerFunctionNode(project: Project, private val function: FunctionConfiguration, region: String): | ||
| AwsExplorerNode<FunctionConfiguration>(project, function, region, SQS_QUEUE_ICON) { //TODO replace to Function icon | ||
|
|
||
| override fun getChildren(): Collection<AbstractTreeNode<Any>> { | ||
| return emptyList() | ||
| } | ||
|
|
||
| override fun toString(): String { | ||
| return function.functionName | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.amazonaws.intellij.s3.explorer | ||
|
|
||
| import com.amazonaws.intellij.ui.S3_BUCKET_ICON | ||
| import com.amazonaws.intellij.ui.S3_SERVICE_ICON | ||
| import com.amazonaws.intellij.ui.explorer.AwsExplorerNode | ||
| import com.amazonaws.intellij.ui.explorer.AwsExplorerServiceRootNode | ||
| import com.amazonaws.services.s3.AmazonS3 | ||
| import com.amazonaws.services.s3.AmazonS3ClientBuilder | ||
| import com.amazonaws.services.s3.model.Bucket | ||
| import com.intellij.ide.util.treeView.AbstractTreeNode | ||
| import com.intellij.openapi.project.Project | ||
|
|
||
| class AwsExplorerS3RootNode(project: Project, region: String): | ||
| AwsExplorerServiceRootNode<Bucket>(project, "Amazon S3", region, S3_SERVICE_ICON) { | ||
|
|
||
| //TODO use a ClientFactory instead | ||
| private var client: AmazonS3 = AmazonS3ClientBuilder.standard() | ||
| .withRegion(region) | ||
| .build() | ||
|
|
||
| //TODO we need to load all the buckets | ||
| override fun loadResources(): Collection<Bucket> { | ||
| return client.listBuckets() | ||
| } | ||
|
|
||
| override fun mapResourceToNode(resource: Bucket) = AwsExplorerBucketNode(project!!, resource, region) | ||
| } | ||
|
|
||
| class AwsExplorerBucketNode(project: Project, private val bucket: Bucket, region: String): | ||
| AwsExplorerNode<Bucket>(project, bucket, region, S3_BUCKET_ICON) { | ||
|
|
||
| override fun getChildren(): Collection<AbstractTreeNode<Any>> { | ||
| return emptyList() | ||
| } | ||
|
|
||
| override fun toString(): String { | ||
| return bucket.name | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.amazonaws.intellij.ui.explorer | ||
|
|
||
| import com.intellij.openapi.project.DumbAware | ||
| import com.intellij.openapi.project.Project | ||
| import com.intellij.openapi.ui.SimpleToolWindowPanel | ||
| import com.intellij.openapi.wm.ToolWindow | ||
| import com.intellij.openapi.wm.ToolWindowFactory | ||
| import com.intellij.ui.components.panels.Wrapper | ||
|
|
||
| class AwsExplorerFactory : ToolWindowFactory, DumbAware { | ||
|
|
||
| override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { | ||
| toolWindow.component.parent.add(ExplorerToolWindow(project)) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| package com.amazonaws.intellij.ui.explorer | ||
|
|
||
| import com.amazonaws.intellij.core.region.AwsRegionManager | ||
| import com.amazonaws.intellij.ui.AWS_ICON | ||
| import com.intellij.ide.projectView.PresentationData | ||
| import com.intellij.ide.util.treeView.AbstractTreeNode | ||
| import com.intellij.openapi.project.Project | ||
| import com.intellij.openapi.util.ClearableLazyValue | ||
| import com.intellij.ui.SimpleTextAttributes | ||
| import javax.swing.Icon | ||
|
|
||
| /** | ||
| * Created by zhaoxiz on 7/27/17. | ||
| */ | ||
| abstract class AwsExplorerNode<T>(project: Project, value: T, val region: String, val awsIcon: Icon?): | ||
| AbstractTreeNode<T>(project, value) { | ||
|
|
||
| override fun update(presentation: PresentationData?) { | ||
| presentation?.setIcon(awsIcon) | ||
| } | ||
|
|
||
| override fun toString() = value.toString() | ||
| } | ||
|
|
||
| class AwsExplorerRootNode(project: Project, region: String): | ||
| AwsExplorerNode<String>(project, "ROOT", region, AWS_ICON) { | ||
|
|
||
| override fun getChildren(): Collection<AbstractTreeNode<String>> { | ||
| val childrenList = mutableListOf<AbstractTreeNode<String>>() | ||
| AwsExplorerService.values() | ||
| .filter { AwsRegionManager.isServiceSupported(region, it.serviceId) } | ||
| .mapTo(childrenList) { it.buildServiceRootNode(project!!, region) } | ||
|
|
||
| return childrenList | ||
| } | ||
| } | ||
|
|
||
| abstract class AwsExplorerServiceRootNode<Resource>(project: Project, value: String, region: String, awsIcon: Icon): | ||
| AwsExplorerNode<String>(project, value, region, awsIcon) { | ||
| val cache: ClearableLazyValue<Collection<AwsExplorerNode<*>>> | ||
|
|
||
| init { | ||
| cache = object : ClearableLazyValue<Collection<AwsExplorerNode<*>>>() { | ||
| override fun compute(): Collection<AwsExplorerNode<*>> { | ||
| return try { | ||
| val resources = loadResources() | ||
| if (resources.isEmpty()) { | ||
| // Return EmptyNode as the single node of the list | ||
| listOf(AwsExplorerEmptyNode(project, region)) | ||
| } else { | ||
| resources.map { mapResourceToNode(it) } | ||
| } | ||
| } catch (e: Exception) { | ||
| // Return the ErrorNode as the single Node of the list | ||
| listOf(AwsExplorerErrorNode(project, e, region)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override fun getChildren(): Collection<AwsExplorerNode<*>> { | ||
| return cache.value | ||
| } | ||
|
|
||
| // This method may throw RuntimeException, must handle it | ||
| abstract fun loadResources(): Collection<Resource> | ||
|
|
||
| abstract fun mapResourceToNode(resource: Resource): AwsExplorerNode<Resource> | ||
| } | ||
|
|
||
| class AwsExplorerErrorNode(project: Project, exception: Exception, region: String): | ||
| AwsExplorerNode<Exception>(project, exception, region, null) { | ||
|
|
||
| override fun getChildren(): Collection<AbstractTreeNode<Any>> { | ||
| return emptyList() | ||
| } | ||
|
|
||
| override fun toString(): String { | ||
| return "Error Loading Resources..." | ||
| } | ||
|
|
||
| override fun update(presentation: PresentationData?) { | ||
| super.update(presentation) | ||
| presentation?.tooltip = value.message | ||
| presentation?.addText(toString(), SimpleTextAttributes.ERROR_ATTRIBUTES) | ||
| } | ||
| } | ||
|
|
||
| class AwsExplorerEmptyNode(project: Project, region: String): AwsExplorerNode<String>(project, "empty", region, null) { | ||
|
|
||
| override fun getChildren(): Collection<AbstractTreeNode<Any>> { | ||
| return emptyList() | ||
| } | ||
|
|
||
| override fun update(presentation: PresentationData?) { | ||
| super.update(presentation) | ||
| presentation?.addText(toString(), SimpleTextAttributes.GRAYED_ATTRIBUTES) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.amazonaws.intellij.ui.explorer | ||
|
|
||
| import com.amazonaws.intellij.lambda.explorer.AwsExplorerLambdaRootNode | ||
| import com.amazonaws.intellij.s3.explorer.AwsExplorerS3RootNode | ||
| import com.amazonaws.services.lambda.AWSLambda | ||
| import com.amazonaws.services.s3.AmazonS3 | ||
| import com.intellij.ide.util.treeView.AbstractTreeNode | ||
| import com.intellij.openapi.project.Project | ||
|
|
||
| enum class AwsExplorerService(val serviceId: String) { | ||
| S3(AmazonS3.ENDPOINT_PREFIX) { | ||
| override fun buildServiceRootNode(project: Project, region: String): AwsExplorerS3RootNode { | ||
| return AwsExplorerS3RootNode(project, region) | ||
| } | ||
| }, | ||
| LAMBDA(AWSLambda.ENDPOINT_PREFIX) { | ||
| override fun buildServiceRootNode(project: Project, region: String): AwsExplorerLambdaRootNode { | ||
| return AwsExplorerLambdaRootNode(project, region) | ||
| } | ||
| }, | ||
| ; | ||
|
|
||
| abstract fun buildServiceRootNode(project: Project, region: String): AbstractTreeNode<String> | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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