Skip to content

Commit

Permalink
feat: filter resources on explorer (#139)
Browse files Browse the repository at this point in the history
* add configuration in contribute point for settings of filters

* feat: filter resources on explorer

* docs: add description for explorer filters

* chore: add changeset
  • Loading branch information
fossamagna authored Oct 7, 2024
1 parent 8529fec commit fb1be66
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-planes-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"amplify-backend-vscode": minor
---

feat: add resource filters on resource explorer
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,42 @@ AWS Amplify Backend VSCode let you following features.

## Features

### Resource Explorer

The AWS Backend Explorer gives you a view of the AWS resources in Amplify Sandbox environment that you can work with when using the AWS Backend Explorer.You can open the AWS Resource page of your choice in the AWS Console of your browser.

![Amplify Backend Explorer](images/explorer.gif)

#### Filter resources

You can filter resources in the AWS Resource Explorer.
You can then switch which filter to use with the filter switching action.
In addition, in `settings.json` you can define custom filters with a pair of names and an array of AWS resources in the tree, as shown below.

```json
{
"amplifyBackend.explorerFilters": [
{
"name": "simple",
"resources": [
"AWS::AppSync::GraphQLApi",
"Custom::AmplifyDynamoDBTable",
"AWS::Lambda::Function",
"AWS::S3::Bucket"
]
}
]
}
```

#### Switch AWS Profile

You can switch AWS Profile to explor the AWS resources.

![Switch AWS Profile](images/switch_profile.gif)

### Secret in sandbox environment

You can view/add/edit/remove secrets in your sandbox environment.

![Secrets Explorer](images/secrets_explorer.gif)
Expand Down
37 changes: 37 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "amplify-backend-explorer.switchFilter",
"title": "Switch Filter",
"icon": "$(filter)"
},
{
"command": "amplify-backend-explorer.openConsole",
"title": "Open AWS Console"
Expand Down Expand Up @@ -67,6 +72,11 @@
"when": "view == amplify-backend-explorer",
"group": "navigation"
},
{
"command": "amplify-backend-explorer.switchFilter",
"when": "view == amplify-backend-explorer",
"group": "navigation"
},
{
"command": "amplify-backend-secrets-explorer.refresh",
"when": "view == amplify-backend-secrets-explorer",
Expand Down Expand Up @@ -108,6 +118,33 @@
"name": "Amplify Backend Secrets Explorer"
}
]
},
"configuration": {
"title": "Amplify Backend",
"properties": {
"amplifyBackend.explorerFilters": {
"type": "array",
"default": [],
"description": "List of filters to apply to the Amplify Backend Explorer",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Name of filter to apply to the Amplify Backend Explorer"
},
"resources": {
"type": "array",
"description": "AWS resource types shown in the Amplify Backend Explorer",
"items": {
"type": "string"
}
}
}
}
}
}
}
},
"repository": {
Expand Down
4 changes: 4 additions & 0 deletions src/console-url-builder/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { StackResource } from "@aws-sdk/client-cloudformation";

export function isSupportedResourceType(resourceType: string) {
return urlBuilders[resourceType] !== undefined;
}

export function buildUrl(
stackResource: Pick<
StackResource,
Expand Down
19 changes: 16 additions & 3 deletions src/explorer/amplify-backend-tree-data-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { AmplifyBackendBaseNode } from "./amplify-backend-base-node";
import { isStackNode } from "./utils";
import { detectAmplifyProjects } from "./amplify-project-detector";
import { AmplifyProject, getAmplifyProject } from "../project";
import {
DefaultResourceFilterProvider,
ResourceFilterProvider,
} from "./resource-filter";

export class AmplifyBackendTreeDataProvider
implements vscode.TreeDataProvider<AmplifyBackendBaseNode>
Expand All @@ -23,7 +27,10 @@ export class AmplifyBackendTreeDataProvider
AmplifyBackendBaseNode | undefined | void
> = this._onDidChangeTreeData.event;

constructor(private workspaceRoot: string) {}
constructor(
private workspaceRoot: string,
private resourceFilterProvider: ResourceFilterProvider
) {}

refresh() {
this._onDidChangeTreeData.fire();
Expand Down Expand Up @@ -56,7 +63,8 @@ export class AmplifyBackendTreeDataProvider
if (!response.StackResources) {
return [];
}
return response.StackResources.map((resource) => {
const predicate = this.resourceFilterProvider.getResourceFilterPredicate();
return response.StackResources.filter(predicate).map((resource) => {
return new AmplifyBackendResourceTreeNode(
resource.LogicalResourceId!,
resource.ResourceType!,
Expand All @@ -75,7 +83,12 @@ export class AmplifyBackendTreeDataProvider

const children: AmplifyBackendBaseNode[] = [];
const profile = Auth.instance.getProfile();
children.push(new AuthNode(`Connected with profile: ${profile}`, profile));
const filterName = this.resourceFilterProvider.getResourceFilterName();
const label =
filterName === DefaultResourceFilterProvider.NONE_FILTER_NAME
? `Connected with profile: ${profile}`
: `Connected with profile ${profile} and resources filtered with ${filterName}`;
children.push(new AuthNode(label, profile));
if (nodes.length) {
children.push(...nodes);
} else {
Expand Down
87 changes: 87 additions & 0 deletions src/explorer/resource-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as vscode from "vscode";
import { StackResource } from "@aws-sdk/client-cloudformation";
import { isSupportedResourceType } from "../console-url-builder";

export type ResourceFilter = {
name: string;
resources: string[];
};

export const getResourceFilters = () => {
const config = vscode.workspace.getConfiguration();
return config.get<ResourceFilter[]>("amplifyBackend.explorerFilters") ?? [];
};

export type ResourceFilterPredicate = (resource: StackResource) => boolean;

const stackPredicate = (resource: StackResource) =>
"AWS::CloudFormation::Stack" === resource.ResourceType;

const defaultPredicate = (resource: StackResource) =>
isSupportedResourceType(resource.ResourceType!);

const or =
(predicates: ResourceFilterPredicate[]) => (resource: StackResource) =>
predicates.some((predicate) => predicate(resource));

const all = () => true;

export interface ResourceFilterProvider {
getResourceFilterName(): string;
setResourceFilterName(filterName: string): Thenable<void>;
getResourceFilterPredicate(): ResourceFilterPredicate;
}

export class DefaultResourceFilterProvider implements ResourceFilterProvider {
static readonly NONE_FILTER_NAME = "none";
private static readonly PRESET_FILTER_NAME = "default";
private static readonly KEY_FILTER_NAME = "filterName";

private readonly state: vscode.Memento;

constructor(state: vscode.Memento) {
this.state = state;
}

getResourceFilterName(): string {
return this.state.get(
DefaultResourceFilterProvider.KEY_FILTER_NAME,
DefaultResourceFilterProvider.NONE_FILTER_NAME
);
}

setResourceFilterName(filterName: string): Thenable<void> {
return this.state.update(
DefaultResourceFilterProvider.KEY_FILTER_NAME,
filterName
);
}

getResourceFilterPredicate(): ResourceFilterPredicate {
const filters = getResourceFilters();
const filterName = this.getResourceFilterName();
if (filterName === DefaultResourceFilterProvider.NONE_FILTER_NAME) {
return all;
}
if (filterName === DefaultResourceFilterProvider.PRESET_FILTER_NAME) {
return or([stackPredicate, defaultPredicate]);
}
const filter = filters.find((filter) => filter.name === filterName);
if (!filter) {
return all;
}
return or([
stackPredicate,
(resource: StackResource) =>
filter.resources.includes(resource.ResourceType!),
]);
}

getResourceFilterNames(): string[] {
return [
DefaultResourceFilterProvider.NONE_FILTER_NAME,
DefaultResourceFilterProvider.PRESET_FILTER_NAME,
...getResourceFilters().map((filter) => filter.name),
];
}
}
28 changes: 27 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SecretsTreeDataProvider } from "./secrets/secrets-tree-data-provider";
import { editSecretCommand } from "./secrets/edit-secret-command";
import { removeSecretCommand } from "./secrets/remove-secret-command";
import { addSecretCommand } from "./secrets/add-secret-command";
import { DefaultResourceFilterProvider } from "./explorer/resource-filter";

export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
Expand Down Expand Up @@ -34,8 +35,13 @@ export function activate(context: vscode.ExtensionContext) {
? vscode.workspace.workspaceFolders[0].uri.fsPath
: undefined;

const resourceFilterProvider = new DefaultResourceFilterProvider(
context.workspaceState
);

const amplifyBackendTreeDataProvider = new AmplifyBackendTreeDataProvider(
rootPath || ""
rootPath || "",
resourceFilterProvider
);
vscode.window.createTreeView("amplify-backend-explorer", {
treeDataProvider: amplifyBackendTreeDataProvider,
Expand Down Expand Up @@ -103,6 +109,26 @@ export function activate(context: vscode.ExtensionContext) {
}
)
);

context.subscriptions.push(
vscode.commands.registerCommand(
"amplify-backend-explorer.switchFilter",
async () => {
const names = resourceFilterProvider.getResourceFilterNames();
const quickPick = vscode.window.createQuickPick();
quickPick.items = names.map((label) => ({ label }));
quickPick.onDidChangeSelection((selection) => {
if (selection[0]) {
resourceFilterProvider.setResourceFilterName(selection[0].label);
quickPick.hide();
amplifyBackendTreeDataProvider.refresh();
}
});
quickPick.onDidHide(() => quickPick.dispose());
quickPick.show();
}
)
);
}

export function deactivate() {}

0 comments on commit fb1be66

Please sign in to comment.