Skip to content

Commit 9f37284

Browse files
vrubezhnydatho7561
authored andcommitted
Add tree elements including the devfile commands in component views #2811
Fixes: #2811 Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
1 parent 9aa1bc4 commit 9f37284

File tree

6 files changed

+194
-22
lines changed

6 files changed

+194
-22
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@
495495
"title": "Open in Browser",
496496
"category": "OpenShift"
497497
},
498+
{
499+
"command": "openshift.component.commands.command.run",
500+
"title": "Run Command",
501+
"category": "OpenShift",
502+
"icon": "$(notebook-execute)"
503+
},
498504
{
499505
"command": "openshift.open.developerConsole",
500506
"title": "Open Console Dashboard",
@@ -990,6 +996,10 @@
990996
"command": "openshift.component.followLog",
991997
"when": "view == openshiftProjectExplorer"
992998
},
999+
{
1000+
"command": "openshift.component.commands.command.run",
1001+
"when": "view == openshiftComponentsView"
1002+
},
9931003
{
9941004
"command": "openshift.open.developerConsole",
9951005
"when": "view == openshiftProjectExplorer"
@@ -1622,6 +1632,11 @@
16221632
"submenu": "serverlessfunction/removeConfig",
16231633
"when": "view == openshiftServerlessFunctionsView && viewItem =~ /^(localFunctionsWithBuild|localDeployFunctions)$/",
16241634
"group": "c1@2"
1635+
},
1636+
{
1637+
"command": "openshift.component.commands.command.run",
1638+
"when": "view == openshiftComponentsView && viewItem =~ /openshift\\-component-command.*\\.dev-run.*/",
1639+
"group": "inline"
16251640
}
16261641
]
16271642
},

src/componentsView.ts

Lines changed: 143 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,142 @@ import * as path from 'path';
77
import * as vsc from 'vscode';
88
import { BaseTreeDataProvider } from './base/baseTreeDataProvider';
99
import { ComponentWorkspaceFolder, OdoWorkspace } from './odo/workspace';
10+
import { Command, CommandProvider } from './odo/componentTypeDescription';
1011
import { Component } from './openshift/component';
1112
import { vsCommand } from './vscommand';
13+
import { ThemeIcon } from 'vscode';
14+
15+
interface ComponentInfo extends ComponentWorkspaceFolder {
16+
getParent(): ComponentInfo;
17+
18+
getChildren() : ComponentInfo[];
19+
20+
toTreeItem() : ComponentWorkspaceFolderTreeItem;
21+
}
22+
23+
abstract class ComponentInfo implements ComponentInfo {
24+
parent : ComponentInfo | null;
25+
26+
constructor(parent : ComponentInfo, folder : ComponentWorkspaceFolder) {
27+
this.parent = parent;
28+
this.component = folder.component;
29+
this.contextPath = folder.contextPath;
30+
}
31+
32+
getParent(): ComponentInfo {
33+
return this.parent;
34+
}
35+
36+
getChildren(): ComponentInfo[] {
37+
return [];
38+
}
39+
}
40+
41+
class ComponentInfoCommand extends ComponentInfo implements CommandProvider {
42+
command :Command;
43+
44+
private static icon = new ThemeIcon('terminal-view-icon');
45+
46+
constructor(parent : ComponentInfo, command : Command) {
47+
super(parent, parent);
48+
this.command = command;
49+
}
50+
51+
getCommand(): Command {
52+
return this.command;
53+
}
54+
55+
toTreeItem() : ComponentWorkspaceFolderTreeItem {
56+
return {
57+
label: this.command.id,
58+
workspaceFolder: this,
59+
tooltip: `Command: ${this.command.id}`,
60+
contextValue: `openshift-component-command${Component.generateContextStateSuffixValue(this)}`,
61+
iconPath: ComponentInfoCommand.icon,
62+
collapsibleState: vsc.TreeItemCollapsibleState.None
63+
};
64+
}
65+
}
66+
67+
class ComponentInfoCommands extends ComponentInfo {
68+
private children : ComponentInfo[];
69+
70+
constructor (parent : ComponentInfo) {
71+
super(parent, parent);
72+
}
73+
74+
getChildren(): ComponentInfo[] {
75+
if (!this.children) {
76+
const thisCommands = this.component.devfileData.devfile.commands;
77+
if (thisCommands === undefined) {
78+
this.children = [];
79+
} else {
80+
this.children = thisCommands.flatMap(c => new ComponentInfoCommand(this, c) );
81+
}
82+
}
83+
return this.children;
84+
}
85+
86+
toTreeItem() : ComponentWorkspaceFolderTreeItem {
87+
return {
88+
label: 'Commands',
89+
workspaceFolder: this,
90+
tooltip: 'Commands',
91+
contextValue: 'openshift-component-commands',
92+
collapsibleState: vsc.TreeItemCollapsibleState.Collapsed
93+
};
94+
}
95+
}
96+
97+
class ComponentInfoRoot extends ComponentInfo {
98+
private children : ComponentInfo[];
99+
100+
constructor (folder : ComponentWorkspaceFolder) {
101+
super(null, folder);
102+
}
103+
104+
getParent(): ComponentInfo {
105+
return this;
106+
}
107+
108+
getChildren(): ComponentInfo[] {
109+
if (!this.children) {
110+
const thisCommands = this.component.devfileData.devfile.commands;
111+
if (thisCommands === undefined) {
112+
this.children = [];
113+
} else {
114+
this.children = [ new ComponentInfoCommands(this) ];
115+
}
116+
}
117+
return this.children;
118+
}
119+
120+
toTreeItem() : ComponentWorkspaceFolderTreeItem {
121+
const tooltip = ['Component',
122+
`Name: ${this.component.devfileData.devfile.metadata.name}`,
123+
`Context: ${this.contextPath}`,
124+
].join('\n');
125+
126+
return {
127+
label: Component.renderLabel(this),
128+
workspaceFolder: this,
129+
tooltip,
130+
contextValue: Component.generateContextValue(this),
131+
iconPath: vsc.Uri.file(path.join(__dirname, '../../images/component', 'workspace.png')),
132+
collapsibleState: vsc.TreeItemCollapsibleState.Collapsed
133+
};
134+
}
135+
}
12136

13137
export interface ComponentWorkspaceFolderTreeItem extends vsc.TreeItem {
14138
workspaceFolder: ComponentWorkspaceFolder;
15139
}
16140

17-
export class ComponentsTreeDataProvider extends BaseTreeDataProvider<ComponentWorkspaceFolder> {
141+
export class ComponentsTreeDataProvider extends BaseTreeDataProvider<ComponentInfo> {
18142

19143
static dataProviderInstance: ComponentsTreeDataProvider;
20144
public odoWorkspace = new OdoWorkspace();
145+
private children : ComponentInfo[];
21146

22147
private constructor() {
23148
super();
@@ -30,8 +155,9 @@ export class ComponentsTreeDataProvider extends BaseTreeDataProvider<ComponentWo
30155
private refresh(contextPath?: string): void {
31156
if (contextPath) {
32157
const folder = this.odoWorkspace.findComponent(vsc.workspace.getWorkspaceFolder(vsc.Uri.parse(contextPath)));
33-
this.onDidChangeTreeDataEmitter.fire(folder)
158+
this.onDidChangeTreeDataEmitter.fire(new ComponentInfoRoot(folder));
34159
} else {
160+
this.children = undefined; // Invalidate children cache so they wll be re-created
35161
this.odoWorkspace.reset();
36162
this.onDidChangeTreeDataEmitter.fire(undefined);
37163
}
@@ -48,7 +174,7 @@ export class ComponentsTreeDataProvider extends BaseTreeDataProvider<ComponentWo
48174
await vsc.commands.executeCommand('revealInExplorer', vsc.Uri.parse(context.contextPath));
49175
}
50176

51-
createTreeView(id: string): vsc.TreeView<ComponentWorkspaceFolder> {
177+
createTreeView(id: string): vsc.TreeView<ComponentInfo> {
52178
if (!this.treeView) {
53179
this.treeView = vsc.window.createTreeView(id, {
54180
treeDataProvider: this,
@@ -64,25 +190,24 @@ export class ComponentsTreeDataProvider extends BaseTreeDataProvider<ComponentWo
64190
return ComponentsTreeDataProvider.dataProviderInstance;
65191
}
66192

67-
getTreeItem(element: ComponentWorkspaceFolder): ComponentWorkspaceFolderTreeItem {
68-
const tooltip = ['Component',
69-
`Name: ${element.component.devfileData.devfile.metadata.name}`,
70-
`Context: ${element.contextPath}`,
71-
].join('\n');
72-
return {
73-
label: Component.renderLabel(element),
74-
workspaceFolder: element,
75-
tooltip,
76-
contextValue: Component.generateContextValue(element),
77-
iconPath: vsc.Uri.file(path.join(__dirname, '../../images/component', 'workspace.png'))
78-
};
193+
getTreeItem(element: ComponentInfo): ComponentWorkspaceFolderTreeItem {
194+
return element.toTreeItem();
79195
}
80196

81-
getChildren(element?: ComponentWorkspaceFolder): vsc.ProviderResult<ComponentWorkspaceFolder[]> {
82-
const result = element ? [] : this.odoWorkspace.getComponents();
197+
getChildren(element?: ComponentInfo): vsc.ProviderResult<ComponentInfo[]> {
198+
if (element) {
199+
return Promise.resolve(element.getChildren());
200+
}
201+
202+
if (this.children) {
203+
return Promise.resolve(this.children);
204+
}
205+
206+
const result = this.odoWorkspace.getComponents();
83207
return Promise.resolve(result).then(async result1 => {
84208
await vsc.commands.executeCommand('setContext', 'openshift.component.explorer.init', !result1?.length && result1.length === 0);
85-
return result;
209+
this.children = result1.flatMap(f => new ComponentInfoRoot(f));
210+
return this.children;
86211
})
87212
}
88213
}

src/odo/command.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,8 @@ export class Command {
311311
undefined,
312312
[new CommandOption('-o json')]);
313313
}
314+
315+
static runComponentCommand(commandId : string): CommandText {
316+
return new CommandText('odo run', commandId);
317+
}
314318
}

src/odo/componentTypeDescription.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ export interface Command {
125125
id: string;
126126
}
127127

128+
export interface CommandProvider {
129+
getCommand() : Command | undefined
130+
}
131+
128132
export interface Exec {
129133
commandLine: string;
130134
component: string;

src/openshift/component.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as YAML from 'yaml';
1313
import { CliChannel } from '../cli';
1414
import { Command } from '../odo/command';
1515
import { ascDevfileFirst, ComponentTypeAdapter, ComponentTypeDescription } from '../odo/componentType';
16-
import { StarterProject } from '../odo/componentTypeDescription';
16+
import { StarterProject, CommandProvider } from '../odo/componentTypeDescription';
1717
import { ComponentWorkspaceFolder } from '../odo/workspace';
1818
import * as odo3 from '../odo3';
1919
import sendTelemetry, { NewComponentCommandProps } from '../telemetry';
@@ -121,7 +121,7 @@ export class Component extends OpenShiftItem {
121121
return state;
122122
}
123123

124-
public static generateContextValue(folder: ComponentWorkspaceFolder): string {
124+
public static generateContextStateSuffixValue(folder: ComponentWorkspaceFolder): string {
125125
const state = Component.componentStates.get(folder.contextPath);
126126
let contextSuffix = '';
127127
if (state.devStatus) {
@@ -133,7 +133,11 @@ export class Component extends OpenShiftItem {
133133
if (state.deployStatus) {
134134
contextSuffix = contextSuffix.concat('.').concat(state.deployStatus);
135135
}
136-
return `openshift.component${contextSuffix}`;
136+
return contextSuffix;
137+
}
138+
139+
public static generateContextValue(folder: ComponentWorkspaceFolder): string {
140+
return `openshift.component${this.generateContextStateSuffixValue(folder)}`;
137141
}
138142

139143
public static renderLabel(folder: ComponentWorkspaceFolder) {
@@ -864,4 +868,24 @@ export class Component extends OpenShiftItem {
864868
}
865869
}
866870
}
871+
872+
@vsCommand('openshift.component.commands.command.run', true)
873+
static runComponentCommand(componentFolder: ComponentWorkspaceFolder): Promise<void> {
874+
const componentName = componentFolder.component.devfileData.devfile.metadata.name;
875+
if ('getCommand' in componentFolder) {
876+
const componentCommand = (<CommandProvider>componentFolder).getCommand();
877+
const command = Command.runComponentCommand(componentCommand.id);
878+
if (Component.isUsingWebviewEditor()) {
879+
DescribeViewLoader.loadView(`Component ${componentName}: Run '${componentCommand.id}' Command`, command, componentFolder);
880+
} else {
881+
void Component.odo.executeInTerminal(
882+
command,
883+
componentFolder.contextPath,
884+
`OpenShift: Component ${componentName}: Run '${componentCommand.id}' Command`);
885+
}
886+
} else {
887+
void window.showErrorMessage(`No Command found in Component '${componentName}`);
888+
}
889+
return;
890+
}
867891
}

0 commit comments

Comments
 (0)