Skip to content

Commit

Permalink
Improve of KafkaExplorer refresh
Browse files Browse the repository at this point in the history
Fixes #61

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Jan 11, 2021
1 parent d814bfa commit 50def4b
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 97 deletions.
94 changes: 78 additions & 16 deletions src/explorer/kafkaExplorer.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,119 @@
import * as vscode from "vscode";

import { Cluster, ClientAccessor } from "../client";
import { ClientAccessor } from "../client";
import { WorkspaceSettings, ClusterSettings } from "../settings";
import { InformationItem } from "./models/common";
import { NodeBase } from "./models/nodeBase";
import { TreeView } from "vscode";
import { KafkaModel } from "./models/kafka";
import { ClusterItem } from "./models/cluster";

const TREEVIEW_ID = 'kafkaExplorer';

/**
* Kafka explorer to show in a tree clusters, topics.
*/
export class KafkaExplorer implements vscode.Disposable, vscode.TreeDataProvider<NodeBase> {

private onDidChangeTreeDataEvent: vscode.EventEmitter<NodeBase | undefined>
= new vscode.EventEmitter<NodeBase | undefined>();
public onDidChangeTreeData?: vscode.Event<NodeBase | null | undefined> | undefined
readonly onDidChangeTreeData?: vscode.Event<NodeBase | null | undefined> | undefined
= this.onDidChangeTreeDataEvent.event;

private readonly clusterSettings: ClusterSettings;
private readonly clientAccessor: ClientAccessor;

protected tree: TreeView<NodeBase> | undefined;

private root: KafkaModel | null;

constructor(
settings: WorkspaceSettings,
settings: WorkspaceSettings,
clusterSettings: ClusterSettings,
clientAccessor: ClientAccessor) {
this.clusterSettings = clusterSettings;
this.clientAccessor = clientAccessor;
this.root = null;
this.tree = vscode.window.createTreeView(TREEVIEW_ID, {
treeDataProvider: this
});
}

public refresh(): void {
// reset the kafka model
this.root = null;
// refresh the treeview
this.onDidChangeTreeDataEvent.fire(undefined);
this.show();
}

private show(): void {
vscode.commands.executeCommand(`${TREEVIEW_ID}.focus`);
}

public getTreeItem(element: NodeBase): vscode.TreeItem | Thenable<vscode.TreeItem> {
return element.getTreeItem();
}

public getChildren(element?: NodeBase): vscode.ProviderResult<NodeBase[]> {
const clusters = this.clusterSettings.getAll();
async getChildren(element?: NodeBase): Promise<NodeBase[]> {
if (!element) {
if (!this.root) {
this.root = new KafkaModel(this.clusterSettings, this.clientAccessor);
}
element = this.root;
}
return element.getChildren();
}

if (clusters.length === 0) {
return [new InformationItem("No clusters added")];
public getParent(element: NodeBase): NodeBase | undefined {
if (element instanceof ClusterItem) {
return undefined;
}
return element.getParent();
}

if (!element) {
return this.getGroupChildren(clusters);
public dispose(): void {
if (this.root) {
this.root.dispose();
}
}

return element.getChildren(element);
/**
* Select the given cluster name in the tree.
*
* @param clusterName the cluster name to select.
*/
async selectClusterByName(clusterName: string): Promise<void> {
const clusterItem = await this.root?.findClusterItemByName(clusterName);
if (!clusterItem) {
return;
}
this.selectItem(clusterItem);
}

public dispose(): void {
// noop
/**
* Select the given topic name which belongs to the given cluster in the tree.
*
* @param clusterName the owner cluster name
* @param topicName the topic name
*/
async selectTopic(clusterName: string, topicName: string): Promise<void> {
const clusterItem = await this.root?.findClusterItemByName(clusterName);
if (!clusterItem) {
return;
}
const topicItem = await (<ClusterItem>clusterItem).findTopictemByName(topicName);
if (!topicItem) {
return;
}
this.selectItem(topicItem);
}

private getGroupChildren(clusters: Cluster[]): NodeBase[] {
return clusters.map((c) => {
return new ClusterItem(this.clientAccessor.get(c.id), c);
private selectItem(item: NodeBase): void {
this.show();
this.tree?.reveal(item, {
select: true,
expand: true,
focus: true
});
}
}
32 changes: 21 additions & 11 deletions src/explorer/models/brokers.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
import * as vscode from "vscode";

import { Broker, Client } from "../../client";
import { Broker } from "../../client";
import { Icons } from "../../constants";
import { ConfigsItem, ExplorerContext } from "./common";
import { ClusterItem } from "./cluster";
import { ConfigsItem } from "./common";
import { NodeBase } from "./nodeBase";

export class BrokerGroupItem extends NodeBase {
public contextValue = "brokers";
public label = "Brokers";
public collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;

constructor(private client: Client, public clusterContext: ExplorerContext) {
super();
constructor(parent: ClusterItem) {
super(parent);
}

public async getChildren(element: NodeBase): Promise<NodeBase[]> {
const brokers = await this.client.getBrokers();
public async computeChildren(): Promise<NodeBase[]> {
const client = this.getParent().client;
const brokers = await client.getBrokers();
return brokers.map((broker) => {
return new BrokerItem(this.client, broker);
return new BrokerItem(broker, this);
});
}
getParent(): ClusterItem {
return <ClusterItem>super.getParent();
}
}

export class BrokerItem extends NodeBase {
public contextValue = "broker";
public collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;

constructor(private client: Client, public broker: Broker) {
super();
constructor(public broker: Broker, public brokerItem: BrokerGroupItem) {
super(brokerItem);
this.label = `${broker.id} (${broker.host}:${broker.port})`;

if (broker.isController) {
Expand All @@ -37,8 +42,13 @@ export class BrokerItem extends NodeBase {
this.iconPath = Icons.Server;
}

getChildren(element: NodeBase): Promise<NodeBase[]> {
const configNode = new ConfigsItem(() => this.client.getBrokerConfigs(this.broker.id));
computeChildren(): Promise<NodeBase[]> {
const client = this.getParent().getParent().client;
const configNode = new ConfigsItem(() => client.getBrokerConfigs(this.broker.id), this);
return Promise.resolve([configNode]);
}

getParent(): BrokerGroupItem {
return <BrokerGroupItem>super.getParent();
}
}
43 changes: 32 additions & 11 deletions src/explorer/models/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,48 @@ import * as vscode from "vscode";
import { Cluster, Client } from "../../client";
import { NodeBase } from "./nodeBase";
import { BrokerGroupItem } from "./brokers";
import { TopicGroupItem } from "./topics";
import { TopicGroupItem, TopicItem } from "./topics";

import { ConsumerGroupsItem } from "./consumerGroups";
import { ExplorerContext } from "./common";
import { KafkaModel } from "./kafka";
import { Disposable } from "vscode";

export class ClusterItem extends NodeBase {
const TOPIC_INDEX = 1;

export class ClusterItem extends NodeBase implements Disposable {
public contextValue = "cluster";
public collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
public context: ExplorerContext

constructor(private client: Client, public cluster: Cluster) {
super();
constructor(public client: Client, public cluster: Cluster, parent: KafkaModel) {
super(parent);
this.label = cluster.name;
this.description = cluster.bootstrap;
this.context = new ExplorerContext(cluster.id);
}

async getChildren(element: NodeBase): Promise<NodeBase[]> {
async computeChildren(): Promise<NodeBase[]> {
return [
new BrokerGroupItem(this.client, this.context),
new TopicGroupItem(this.client, this.context),
new ConsumerGroupsItem(this.client, this.context)];
new BrokerGroupItem(this),
new TopicGroupItem(this),
new ConsumerGroupsItem(this)];
}

getParent(): KafkaModel {
return <KafkaModel>super.getParent();
}

public dispose(): void {
this.client.dispose();
}

async findTopictemByName(topicName: string): Promise<NodeBase | TopicItem | undefined> {
const topics = (await this.getTopicGroupItem()).getChildren();
return topics
.then(t =>
t.find(child => (<TopicItem>child).topic.id === topicName));
}

private async getTopicGroupItem(): Promise<NodeBase> {
return (await this.getChildren())[TOPIC_INDEX];
}

}
30 changes: 10 additions & 20 deletions src/explorer/models/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@ import { ConfigEntry } from "../../client";
import { Icons } from "../../constants";
import { NodeBase } from "./nodeBase";


/**
* The context for propagating information down the hierarchy.
*/
export class ExplorerContext {
constructor(public clusterId: string) {
this.clusterId = clusterId;
}
}

/**
* A node used to display an error message
*/
Expand All @@ -22,8 +12,8 @@ export class ErrorItem extends NodeBase {
public collapsibleState = vscode.TreeItemCollapsibleState.None;
public iconPath = Icons.Warning;

constructor(message: string) {
super();
constructor(message: string, parent: NodeBase) {
super(parent);
this.label = message;
}
}
Expand All @@ -36,8 +26,8 @@ export class InformationItem extends NodeBase {
public collapsibleState = vscode.TreeItemCollapsibleState.None;
public iconPath = Icons.Information;

constructor(message: string) {
super();
constructor(message: string, parent: NodeBase) {
super(parent);
this.label = message;
}
}
Expand All @@ -50,15 +40,15 @@ export class ConfigsItem extends NodeBase {
public contextValue = "configs";
public collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;

constructor(private provider: () => Promise<ConfigEntry[]>) {
super();
constructor(private provider: () => Promise<ConfigEntry[]>, parent: NodeBase) {
super(parent);
}

async getChildren(element: NodeBase): Promise<NodeBase[]> {
async computeChildren(): Promise<NodeBase[]> {
const configEntries = await this.provider();
return configEntries
.sort((a, b) => (a.configName < b.configName ? -1 : (a.configName > b.configName) ? 1 : 0))
.map((configEntry) => (new ConfigEntryItem(configEntry)));
.map((configEntry) => (new ConfigEntryItem(configEntry, this)));
}
}

Expand All @@ -69,8 +59,8 @@ class ConfigEntryItem extends NodeBase {
public contextValue = "configitem";
public collapsibleState = vscode.TreeItemCollapsibleState.None;

constructor(configEntry: ConfigEntry) {
super();
constructor(configEntry: ConfigEntry, parent: NodeBase) {
super(parent);
this.label = configEntry.configName;
this.description = configEntry.configValue;
}
Expand Down
Loading

0 comments on commit 50def4b

Please sign in to comment.