diff --git a/src/extension.ts b/src/extension.ts index 93c952e..8f8ad58 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -80,7 +80,7 @@ export function activate(context: vscode.ExtensionContext): Promise { registerDisposable( vscode.window.registerTreeDataProvider( 'vscode-dapr.views.applications', - registerDisposable(new DaprApplicationTreeDataProvider(daprApplicationProvider, new LocalDaprInstallationManager())))); + registerDisposable(new DaprApplicationTreeDataProvider(daprApplicationProvider, new LocalDaprInstallationManager(), daprClient)))); registerDisposable( vscode.window.registerTreeDataProvider( diff --git a/src/services/daprClient.ts b/src/services/daprClient.ts index 2d40bd4..d802c4f 100644 --- a/src/services/daprClient.ts +++ b/src/services/daprClient.ts @@ -14,6 +14,7 @@ export interface DaprClient { invokeGet(application: DaprApplication, method: string, token?: vscode.CancellationToken): Promise; invokePost(application: DaprApplication, method: string, payload?: unknown, token?: vscode.CancellationToken): Promise; publishMessage(application: DaprApplication, pubSubName: string, topic: string, payload?: unknown, token?: vscode.CancellationToken): Promise; + getMetadata(application: DaprApplication, token?: vscode.CancellationToken): Promise; } function manageResponse(response: HttpResponse): unknown { @@ -60,4 +61,22 @@ export default class HttpDaprClient implements DaprClient { await this.httpClient.post(url, payload, { json: true }, token); } -} \ No newline at end of file + + async getMetadata(application: DaprApplication, token?: vscode.CancellationToken | undefined): Promise { + const originalUrl = `http://localhost:${application.httpPort}/v1.0/metadata`; + + const response = await this.httpClient.get(originalUrl, { allowRedirects: false }, token); + + return manageResponse(response) as DaprMetadata; + } +} + +export interface DaprMetadata { + components: DaprComponentMetadata[]; + } + + export interface DaprComponentMetadata { + name: string; + type: string; + version: string; + } \ No newline at end of file diff --git a/src/views/applications/daprApplicationNode.ts b/src/views/applications/daprApplicationNode.ts index 880cab3..4deca0c 100644 --- a/src/views/applications/daprApplicationNode.ts +++ b/src/views/applications/daprApplicationNode.ts @@ -4,13 +4,15 @@ import * as vscode from 'vscode'; import TreeNode from "../treeNode"; import { DaprApplication } from '../../services/daprApplicationProvider'; +import DaprComponentsNode from "./daprComponentsNode"; +import { DaprClient } from '../../services/daprClient'; export default class DaprApplicationNode implements TreeNode { - constructor(public readonly application: DaprApplication) { + constructor(public readonly application: DaprApplication, public readonly daprClient: DaprClient) { } getTreeItem(): Promise { - const item = new vscode.TreeItem(this.application.appId); + const item = new vscode.TreeItem(this.application.appId, vscode.TreeItemCollapsibleState.Collapsed); item.contextValue = 'application'; @@ -18,4 +20,8 @@ export default class DaprApplicationNode implements TreeNode { return Promise.resolve(item); } + + getChildren(): TreeNode[] { + return [new DaprComponentsNode(this.application, this.daprClient)]; + } } \ No newline at end of file diff --git a/src/views/applications/daprApplicationTreeDataProvider.ts b/src/views/applications/daprApplicationTreeDataProvider.ts index 8e0c02f..6b6097a 100644 --- a/src/views/applications/daprApplicationTreeDataProvider.ts +++ b/src/views/applications/daprApplicationTreeDataProvider.ts @@ -7,6 +7,7 @@ import TreeNode from '../treeNode'; import DaprApplicationNode from './daprApplicationNode'; import NoApplicationsRunningNode from './noApplicationsRunningNode'; import { DaprInstallationManager } from '../../services/daprInstallationManager'; +import { DaprClient } from '../../services/daprClient'; export default class DaprApplicationTreeDataProvider extends vscode.Disposable implements vscode.TreeDataProvider { private readonly onDidChangeTreeDataEmitter = new vscode.EventEmitter(); @@ -14,7 +15,8 @@ export default class DaprApplicationTreeDataProvider extends vscode.Disposable i constructor( private readonly applicationProvider: DaprApplicationProvider, - private readonly installationManager: DaprInstallationManager) { + private readonly installationManager: DaprInstallationManager, + private readonly daprClient: DaprClient) { super(() => { this.applicationProviderListener.dispose(); this.onDidChangeTreeDataEmitter.dispose(); @@ -34,13 +36,20 @@ export default class DaprApplicationTreeDataProvider extends vscode.Disposable i return element.getTreeItem(); } - async getChildren(): Promise { - const applications = await this.applicationProvider.getApplications(); - - if (applications.length > 0) { - return applications.map(application => new DaprApplicationNode(application)); + async getChildren(element?: TreeNode): Promise { + if (element) { + return element.getChildren?.() ?? []; } else { - return [ new NoApplicationsRunningNode(this.installationManager) ]; + const applications = await this.applicationProvider.getApplications(); + const appNodeList = applications.map(application => new DaprApplicationNode(application, this.daprClient)); + + + if (appNodeList.length > 0) { + return appNodeList; + } else { + return [ new NoApplicationsRunningNode(this.installationManager) ]; + } } + } } \ No newline at end of file diff --git a/src/views/applications/daprComponentsNode.ts b/src/views/applications/daprComponentsNode.ts new file mode 100644 index 0000000..77b59a6 --- /dev/null +++ b/src/views/applications/daprComponentsNode.ts @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import TreeNode from "../treeNode"; +import { DaprApplication } from '../../services/daprApplicationProvider'; +import DaprMetadataNode from './daprMetadataNode'; +import { DaprClient } from '../../services/daprClient'; +import { getLocalizationPathForFile } from '../../util/localization'; + +const localize = nls.loadMessageBundle(getLocalizationPathForFile(__filename)); + + +export default class DaprComponentsNode implements TreeNode { + constructor(private readonly application: DaprApplication, private readonly daprClient: DaprClient) { + } + + getTreeItem(): Promise { + const label = localize('views.applications.daprComponentsNode.componentNode', 'Components'); + + const item = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.Collapsed); + + item.contextValue = 'components'; + + item.iconPath = new vscode.ThemeIcon('archive'); + + return Promise.resolve(item); + } + + async getChildren(): Promise { + const label = localize('views.applications.daprComponentsNode.noComponents', 'There are no components in use.'); + const responseData = await this.daprClient.getMetadata(this.application); + const components = responseData.components; + if(components.length > 0) { + return components.map(comp => new DaprMetadataNode(comp.name, 'database')); + } + return [new DaprMetadataNode(label, 'warning')]; + } +} + diff --git a/src/views/applications/daprMetadataNode.ts b/src/views/applications/daprMetadataNode.ts new file mode 100644 index 0000000..e353874 --- /dev/null +++ b/src/views/applications/daprMetadataNode.ts @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as vscode from 'vscode'; +import TreeNode from "../treeNode"; + +export default class DaprMetadataNode implements TreeNode { + constructor(private readonly metadata: string, private readonly themeIconId: string) { + } + + getTreeItem(): Promise { + const item = new vscode.TreeItem(this.metadata); + + item.contextValue = 'metadata'; + + item.iconPath = new vscode.ThemeIcon(this.themeIconId); + + return Promise.resolve(item); + } + + +} \ No newline at end of file diff --git a/src/views/treeNode.ts b/src/views/treeNode.ts index b5ea9b3..46b87ee 100644 --- a/src/views/treeNode.ts +++ b/src/views/treeNode.ts @@ -5,4 +5,5 @@ import * as vscode from 'vscode'; export default interface TreeNode { getTreeItem(): Promise; + getChildren?: () => TreeNode[] | Promise; }