Skip to content

Commit 3640689

Browse files
Performance improvement of getting devfile registries (#2495)
Signed-off-by: msivasubramaniaan <msivasub@redhat.com>
1 parent 88a14e9 commit 3640689

File tree

7 files changed

+123
-93
lines changed

7 files changed

+123
-93
lines changed

src/extension.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function migrateFromOdo018(): void {
4949
}
5050
}
5151

52-
async function verifyBundledBinaries(): Promise<{odoPath: string, ocPath: string}> {
52+
async function verifyBundledBinaries(): Promise<{ odoPath: string, ocPath: string }> {
5353
return {
5454
odoPath: await ToolsConfig.detect('odo'),
5555
ocPath: await ToolsConfig.detect('oc'),
@@ -63,8 +63,8 @@ export async function activate(extensionContext: ExtensionContext): Promise<any>
6363
migrateFromOdo018();
6464
Cluster.extensionContext = extensionContext;
6565
TokenStore.extensionContext = extensionContext;
66-
const crcStatusItem = window.createStatusBarItem(StatusBarAlignment.Left);
67-
crcStatusItem.command = 'openshift.explorer.stopCluster';
66+
const crcStatusItem = window.createStatusBarItem(StatusBarAlignment.Left);
67+
crcStatusItem.command = 'openshift.explorer.stopCluster';
6868
const disposable = [
6969
...(await registerCommands(
7070
'./k8s/route',
@@ -127,10 +127,10 @@ export async function activate(extensionContext: ExtensionContext): Promise<any>
127127
});
128128

129129
function updateStatusBarItem(statusBarItem: StatusBarItem, text: string): void {
130-
if (!workspace.getConfiguration('openshiftConnector').get('crcBinaryLocation')) {
131-
statusBarItem.hide();
132-
return;
133-
}
130+
if (!workspace.getConfiguration('openshiftConnector').get('crcBinaryLocation')) {
131+
statusBarItem.hide();
132+
return;
133+
}
134134
statusBarItem.text = `$(debug-stop) ${text}`;
135135
statusBarItem.show();
136136
}
@@ -142,6 +142,8 @@ export async function activate(extensionContext: ExtensionContext): Promise<any>
142142
OdoImpl.Instance.loadWorkspaceComponents(event);
143143
});
144144

145+
void ComponentTypesView.instance.getAllComponents();
146+
145147
OdoImpl.Instance.loadWorkspaceComponents(null);
146148

147149
startTelemetry(extensionContext);

src/odo.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ export interface Odo {
338338
getApplicationChildren(application: OpenShiftObject): Promise<OpenShiftObject[]>;
339339
getComponents(application: OpenShiftObject, condition?: (value: OpenShiftObject) => boolean): Promise<OpenShiftObject[]>;
340340
getCompTypesJson():Promise<DevfileComponentType[]>;
341-
getComponentTypesOfJSON(devFileComponents: DevfileComponentType[]):ComponentTypeAdapter[];
342341
getComponentTypes(): Promise<ComponentTypeAdapter[]>;
343342
getComponentChildren(component: OpenShiftObject): Promise<OpenShiftObject[]>;
344343
getRoutes(component: OpenShiftObject): Promise<OpenShiftObject[]>;
@@ -645,16 +644,6 @@ export class OdoImpl implements Odo {
645644
return compTypesJson?.items;
646645
}
647646

648-
public getComponentTypesOfJSON(devFileComponents: DevfileComponentType[]): ComponentType[] {
649-
const devfileItems: ComponentTypeAdapter[] = [];
650-
651-
if (devFileComponents) {
652-
devFileComponents.map((item) => devfileItems.push(new ComponentTypeAdapter(item.Name, undefined, item.Description, undefined, item.Registry.Name)));
653-
}
654-
655-
return devfileItems;
656-
}
657-
658647
public async getComponentTypes(): Promise<ComponentType[]> {
659648
// if kc is produced, KUBECONFIG env var is empty or pointing
660649

src/registriesView.ts

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ import {
1616
} from 'vscode';
1717
import { getInstance, Odo, OdoImpl } from './odo';
1818
import {
19+
ComponentTypeDescription,
20+
DevfileComponentType,
1921
Registry,
2022
} from './odo/componentType';
2123
import { StarterProject } from './odo/componentTypeDescription';
2224
import { vsCommand, VsCommandError } from './vscommand';
2325
import validator from 'validator';
24-
import RegistryViewLoader from './webview/devfile-registry/registryViewLoader';
26+
import { Command } from './odo/command';
27+
import { CliExitData } from './cli';
28+
import { Subject } from 'rxjs';
2529

2630
type ComponentType = Registry;
2731

@@ -45,6 +49,8 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
4549

4650
readonly odo: Odo = getInstance();
4751
private registries: Registry[];
52+
private readonly compDescriptions: Set<ComponentTypeDescription> = new Set<ComponentTypeDescription>();
53+
public subject: Subject<string> = new Subject<string>();
4854

4955
createTreeView(id: string): TreeView<ComponentType> {
5056
if (!this.treeView) {
@@ -97,6 +103,47 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
97103
return this.registries;
98104
}
99105

106+
public getCompDescriptions(): Set<ComponentTypeDescription> {
107+
return this.compDescriptions;
108+
}
109+
110+
public getListOfRegistries(): Registry[] {
111+
return this.registries;
112+
}
113+
114+
public getAllComponents(): void {
115+
let isError = false;
116+
this.compDescriptions.clear();
117+
void getInstance().getCompTypesJson().then(async (devFileComponentTypes: DevfileComponentType[]) => {
118+
const components = new Set<string>(devFileComponentTypes.map((devFileComponentType: DevfileComponentType) => devFileComponentType.Name));
119+
await this.getRegistries();
120+
components.forEach((compName: string) => {
121+
getInstance().execute(Command.describeCatalogComponent(compName)).then((componentDesc: CliExitData) => {
122+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
123+
const out: ComponentTypeDescription[] = JSON.parse(componentDesc.stdout);
124+
out.forEach((component) => {
125+
// eslint-disable-next-line max-nested-callbacks
126+
component.Devfile?.starterProjects?.map((starter: StarterProject) => {
127+
starter.typeName = compName;
128+
});
129+
this.compDescriptions.add(component);
130+
});
131+
if (devFileComponentTypes.length === this.compDescriptions.size) {
132+
this.subject.next('refresh');
133+
}
134+
}).catch(() => {
135+
isError = true;
136+
}).finally(() => {
137+
if (isError && !this.subject.closed) {
138+
this.subject.next('refresh');
139+
}
140+
});
141+
});
142+
}).catch(() => {
143+
this.subject.next('error');
144+
});
145+
}
146+
100147
// eslint-disable-next-line class-methods-use-this
101148
async getChildren(parent: ComponentType): Promise<ComponentType[]> {
102149
let children: ComponentType[] = [];
@@ -227,8 +274,8 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
227274
let token: string;
228275
if (secure === 'Yes') {
229276
token = await window.showInputBox({
230-
placeHolder: 'Token to access the registry',
231-
validateInput: (value) => value?.trim().length > 0 ? undefined : 'Token cannot be empty'
277+
placeHolder: 'Token to access the registry',
278+
validateInput: (value) => value?.trim().length > 0 ? undefined : 'Token cannot be empty'
232279
});
233280
if (!token) return null;
234281
}
@@ -238,17 +285,17 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
238285
*/
239286

240287
if (registryContext) {
241-
const notChangedRegisty = registries.find((registry) => registry.Name === regName && registry.URL === regURL && registry.Secure === (secure === 'Yes'));
242-
if (notChangedRegisty) {
243-
return null;
244-
}
245-
await vscode.commands.executeCommand('openshift.componentTypesView.registry.remove', registryContext, true);
288+
const notChangedRegisty = registries.find((registry) => registry.Name === regName && registry.URL === regURL && registry.Secure === (secure === 'Yes'));
289+
if (notChangedRegisty) {
290+
return null;
291+
}
292+
await vscode.commands.executeCommand('openshift.componentTypesView.registry.remove', registryContext, true);
246293
}
247294

248295
const newRegistry = await OdoImpl.Instance.addRegistry(regName, regURL, token);
249296
ComponentTypesView.instance.addRegistry(newRegistry);
250297

251-
RegistryViewLoader.refresh();
298+
ComponentTypesView.instance.getAllComponents();
252299
}
253300

254301
@vsCommand('openshift.componentTypesView.registry.remove')
@@ -261,7 +308,9 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
261308
if (yesNo === 'Yes') {
262309
await OdoImpl.Instance.removeRegistry(registry.Name);
263310
ComponentTypesView.instance.removeRegistry(registry);
264-
RegistryViewLoader.refresh();
311+
if (!isEdit) {
312+
ComponentTypesView.instance.getAllComponents();
313+
}
265314
}
266315
}
267316

@@ -274,9 +323,4 @@ export class ComponentTypesView implements TreeDataProvider<ComponentType> {
274323
public static async openRegistryWebSite(registry: Registry): Promise<void> {
275324
await commands.executeCommand('vscode.open', Uri.parse(registry.URL));
276325
}
277-
278-
@vsCommand('openshift.componentTypesView.registry.openInView')
279-
public static async openRegistryInWebview(): Promise<void> {
280-
await RegistryViewLoader.loadView('Devfile Registry');
281-
}
282326
}

src/webview/devfile-registry/app/home.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ export const Home: React.FC<DefaultProps> = ({ }) => {
6161
React.useEffect(() => {
6262
return VSCodeMessage.onMessage((message) => {
6363
if (message.data.action === 'getAllComponents') {
64-
if (message.data.error) {
65-
setError(message.data.error);
64+
if (message.data.errorMessage && message.data.errorMessage.length > 0) {
65+
setError(message.data.errorMessage);
6666
} else {
6767
setError('');
6868
message.data.registries.map((registry: Registry) => {
@@ -75,10 +75,10 @@ export const Home: React.FC<DefaultProps> = ({ }) => {
7575
setFilteredcompDescriptions(getFilteredCompDesc(message.data.registries, message.data.compDescriptions, searchValue));
7676
}
7777
} else if (message.data.action === 'loadingComponents') {
78-
setError('');
79-
setFilteredcompDescriptions([]);
80-
setCompDescriptions([]);
81-
setSearchValue('');
78+
setError('');
79+
setFilteredcompDescriptions([]);
80+
setCompDescriptions([]);
81+
setSearchValue('');
8282
}
8383
});
8484
});
@@ -120,9 +120,10 @@ export const Home: React.FC<DefaultProps> = ({ }) => {
120120
/>
121121
}
122122
<HomeItem compDescriptions={filteredcompDescriptions} />
123+
{error?.length > 0 ? <ErrorPage message='Devfiles not downloaded properly' /> : null}
123124
</>
124125
:
125-
error.length > 0 ? <ErrorPage message={error} /> : <LoadScreen />
126+
error?.length > 0 ? <ErrorPage message={error} /> : <LoadScreen />
126127
}
127128
</>
128129
);

src/webview/devfile-registry/app/loading.style.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { Theme, createStyles } from '@material-ui/core/styles';
77

88
export default (theme: Theme) =>
99
createStyles({
10+
loadProgress: {
11+
color: '#EE0000'
12+
},
1013
loading: {
1114
display: 'flex',
1215
justifyContent: 'center',

src/webview/devfile-registry/app/loading.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@
22
* Copyright (c) Red Hat, Inc. All rights reserved.
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
5-
6-
import CircularProgress from '@mui/material/CircularProgress';
7-
import Box from '@mui/material/Box';
85
import React from 'react';
9-
import { makeStyles } from '@material-ui/core';
6+
import { Box, makeStyles, Typography } from '@material-ui/core';
7+
import LinearProgress from '@mui/material/LinearProgress';
108
import loaderStyle from './loading.style';
119

1210
const useLoadingStyle = makeStyles(loaderStyle);
1311

1412
export function LoadScreen() {
1513
const loadingStyle = useLoadingStyle();
1614
return (
17-
<Box className={loadingStyle.loading}>
18-
<CircularProgress color='inherit' />
19-
</Box>
15+
<div className={loadingStyle.loading}>
16+
<div style={{ width: '30rem' }}>
17+
<Box sx={{ color: '#EE0000' }}>
18+
<LinearProgress color='inherit' sx={{height: '1rem'}} />
19+
</Box>
20+
<Typography
21+
variant='caption'
22+
component='div'
23+
color='inherit'
24+
style={{ marginTop: '0.5rem', fontSize: '1.25em' }}
25+
>Loading Registry View</Typography>
26+
</div>
27+
</div>
2028
);
2129
}

src/webview/devfile-registry/registryViewLoader.ts

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,12 @@ import * as vscode from 'vscode';
66
import * as path from 'path';
77
import * as fs from 'fs';
88
import { ExtenisonID } from '../../util/constants';
9-
import { getInstance } from '../../odo';
10-
import { Command } from '../../odo/command';
119
import { stringify } from 'yaml';
12-
import { ComponentTypesView } from '../../registriesView';
13-
import { StarterProject } from '../../odo/componentTypeDescription';
14-
import { DevfileComponentType } from '../../odo/componentType';
10+
import { ComponentTypesView } from '../../registriesView'
1511
import { vsCommand } from '../../vscommand';
1612
import { ExtCommandTelemetryEvent } from '../../telemetry';
1713

1814
let panel: vscode.WebviewPanel;
19-
let compDescriptions = new Set<any>();
2015

2116
async function devfileRegistryViewerMessageListener(event: any): Promise<any> {
2217
let starterProject = event.selectedProject;
@@ -110,53 +105,41 @@ export default class RegistryViewLoader {
110105
.replace('<!-- meta http-equiv="Content-Security-Policy" -->', meta);
111106
}
112107

108+
@vsCommand('openshift.componentTypesView.registry.openInView')
109+
public static async openRegistryInWebview(): Promise<void> {
110+
await RegistryViewLoader.loadView('Devfile Registry');
111+
}
112+
113113
@vsCommand('openshift.componentTypesView.registry.closeView')
114114
static async closeRegistryInWebview(): Promise<void> {
115115
panel?.dispose();
116116
}
117117

118118
static refresh(): void {
119-
if(panel) {
120-
panel.webview.postMessage({action: 'loadingComponents' });
121-
getAllComponents('getAllComponents');
122-
}
119+
if (panel) {
120+
panel.webview.postMessage({ action: 'loadingComponents' });
121+
}
123122
}
124123
}
125124

126-
function getAllComponents(eventActionName: string) {
127-
compDescriptions.clear();
128-
getInstance().getCompTypesJson().then(async (devFileComponentTypes: DevfileComponentType[]) => {
129-
const components = new Set<string>();
130-
getInstance().getComponentTypesOfJSON(devFileComponentTypes).map((comp) => {
131-
components.add(comp.name);
132-
});
133-
const registries = await ComponentTypesView.instance.getRegistries();
134-
Array.from(components).map(async (compName: string) => {
135-
getInstance().execute(Command.describeCatalogComponent(compName)).then((componentDesc) => {
136-
const out = JSON.parse(componentDesc.stdout);
137-
out.forEach((component) => {
138-
component.Devfile?.starterProjects?.map((starter: StarterProject) => {
139-
starter.typeName = compName;
140-
});
141-
compDescriptions.add(component);
142-
});
143-
if (devFileComponentTypes.length === compDescriptions.size) {
144-
panel.webview.postMessage(
145-
{
146-
action: eventActionName,
147-
compDescriptions: Array.from(compDescriptions),
148-
registries: registries
149-
}
150-
);
151-
}
152-
}).catch((reason) => {
153-
panel.webview.postMessage(
154-
{
155-
action: eventActionName,
156-
error: '500: Internal Server Error, Please try later'
157-
}
158-
);
159-
});
160-
});
161-
});
125+
function getAllComponents(eventActionName: string, error?: string) {
126+
const registries = ComponentTypesView.instance.getListOfRegistries();
127+
const componentDescriptions = ComponentTypesView.instance.getCompDescriptions();
128+
panel?.webview.postMessage(
129+
{
130+
action: eventActionName,
131+
compDescriptions: Array.from(componentDescriptions),
132+
registries: registries,
133+
errorMessage: error
134+
}
135+
);
162136
}
137+
138+
ComponentTypesView.instance.subject.subscribe((value: string) => {
139+
if (value === 'refresh') {
140+
RegistryViewLoader.refresh();
141+
getAllComponents('getAllComponents');
142+
} else if (value === 'error') {
143+
getAllComponents('getAllComponents', 'Devfile Registry is not accessible');
144+
}
145+
});

0 commit comments

Comments
 (0)