Skip to content

Commit 388ccc6

Browse files
committed
Do not remove folder from workspace when delete application
This PR fixes #2022. Signed-off-by: Denis Golovin [email protected]
1 parent ad15708 commit 388ccc6

File tree

3 files changed

+51
-48
lines changed

3 files changed

+51
-48
lines changed

src/odo.ts

+41-39
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ class OdoModel {
460460

461461
public delete(item: OpenShiftObject): void {
462462
this.pathToObject.delete(item.path);
463+
this.parentToChildren.delete(item);
463464
if (item.contextPath) {
464465
this.contextToObject.delete(item.contextPath.fsPath);
465466
this.deleteContext(item.contextPath.fsPath);
@@ -835,49 +836,50 @@ export class OdoImpl implements Odo {
835836
return this.insertAndReveal(new OpenShiftProject(clusters[0], projectName, true));
836837
}
837838

838-
public async deleteApplication(app: OpenShiftObject): Promise<OpenShiftObject> {
839-
const allComps = await OdoImpl.instance.getComponents(app);
840-
const allContexts = [];
841-
let callDelete = false;
842-
allComps.forEach((component) => {
843-
OdoImpl.data.delete(component); // delete component from model
844-
if (!callDelete && component.contextValue === ContextType.COMPONENT_PUSHED || component.contextValue === ContextType.COMPONENT_NO_CONTEXT) {
845-
callDelete = true; // if there is at least one component deployed in application `odo app delete` command should be called
846-
}
847-
if (component.contextPath) { // if component has context folder save it to remove from settings cache
848-
allContexts.push(workspace.getWorkspaceFolder(component.contextPath));
849-
}
839+
public async deleteComponentsWithoutRefresh(components: OpenShiftObject[]): Promise<void> {
840+
let result = Promise.resolve();
841+
components
842+
.forEach((component) => {
843+
result = result.then(async ()=> {
844+
if (component.contextPath) { // call odo only for local components in workspace
845+
await this.execute(
846+
Command.deleteComponent(
847+
component.getParent().getParent().getName(),
848+
component.getParent().getName(), component.getName(),
849+
!!component.contextPath,
850+
component.kind === ComponentKind.S2I
851+
),
852+
component.contextPath ? component.contextPath.fsPath : Platform.getUserHomePath()
853+
);
854+
}
855+
await component.getParent().removeChild(component);
856+
OdoImpl.data.delete(component);
857+
}
858+
);
850859
});
860+
await result;
861+
}
851862

852-
if (callDelete) {
853-
await this.execute(Command.deleteApplication(app.getParent().getName(), app.getName()));
854-
}
855-
// Chain workspace folder deletions, because when updateWorkspaceFoder called next call is possible only after
856-
// listener registered with onDidChangeWorkspaceFolders called.
857-
let result = Promise.resolve();
858-
// To avoid workspace restart during deletion first have to check if there is wsFolder with index 0
859-
const rootFolder = allContexts.find(folder => folder.index === 0);
860-
allContexts.forEach((wsFolder) => {
861-
if (rootFolder !== wsFolder) {
862-
result = result.then(() => {
863-
workspace.updateWorkspaceFolders(wsFolder.index, 1);
864-
return new Promise<void>((resolve) => {
865-
const disposable = workspace.onDidChangeWorkspaceFolders(() => {
866-
disposable.dispose();
867-
resolve();
868-
});
869-
});
870-
});
863+
public async deleteApplication(app: OpenShiftObject): Promise<OpenShiftObject> {
864+
const allComps = await OdoImpl.instance.getApplicationChildren(app);
865+
866+
// find out if there is at least one deployed component/service and `odo app delete` should be called
867+
const callAppDelete = !!allComps.find(
868+
(item) => [ContextType.COMPONENT_PUSHED, ContextType.COMPONENT_NO_CONTEXT, ContextType.SERVICE].includes(item.contextValue)
869+
);
870+
871+
try { // first delete all application related resources in cluster
872+
if (callAppDelete) {
873+
await this.execute(Command.deleteApplication(app.getParent().getName(), app.getName()));
871874
}
872-
});
873-
if (rootFolder) {
874-
result = result.then(() => {
875-
workspace.updateWorkspaceFolders(rootFolder.index, 1)
876-
});
877-
}
878-
return result.then(() => {
875+
await this.deleteComponentsWithoutRefresh(allComps);
879876
return this.deleteAndRefresh(app);
880-
});
877+
} catch (error) {
878+
// if error occurs during application deletion, app object has to be refreshed to new state
879+
this.subject.next(new OdoEventImpl('changed', app));
880+
throw error;
881+
}
882+
881883
}
882884

883885
public async createApplication(application: OpenShiftObject): Promise<OpenShiftObject> {

src/openshift/application.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ export class Application extends OpenShiftItem {
4242
await Progress.execFunctionWithProgress(`Deleting the Application '${appName}'`, () => Application.odo.deleteApplication(application));
4343
return `Application '${appName}' successfully deleted`
4444
} catch (err) {
45-
const telemetryMessage = err instanceof VsCommandError ? err.telemetryMessage : err.message;
45+
const telemetryMessage = err instanceof VsCommandError ? ` ${err.telemetryMessage}` : '';
4646
throw new VsCommandError(`Failed to delete Application with error '${err.message}'`,
47-
`Failed to delete Application with error ${telemetryMessage}`, err);
47+
`Failed to delete Application with error${telemetryMessage}`, err);
4848
}
4949
}
5050
}

test/unit/openshift/application.test.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ suite('OpenShift/Application', () => {
2121
let quickPickStub: sinon.SinonStub;
2222
let sandbox: sinon.SinonSandbox;
2323
let execStub: sinon.SinonStub;
24+
let getProjectsStub: sinon.SinonStub;
2425
const clusterItem = new TestItem(null, 'cluster', ContextType.CLUSTER);
2526
const projectItem = new TestItem(clusterItem, 'project', ContextType.PROJECT);
2627
const appItem = new TestItem(projectItem, 'app', ContextType.APPLICATION);
27-
const compItem = new TestItem(appItem, 'app', ContextType.COMPONENT_NO_CONTEXT, [], null);
28+
const compItem = new TestItem(appItem, 'component', ContextType.COMPONENT_NO_CONTEXT, [], null);
2829
compItem.path = 'path/to/component';
2930
appItem.getChildren().push(compItem);
3031

3132
setup(() => {
3233
sandbox = sinon.createSandbox();
3334
execStub = sandbox.stub(OdoImpl.prototype, 'execute').resolves({error: null, stdout: '', stderr: ''});
3435
sandbox.stub(OdoImpl.prototype, 'getClusters').resolves([clusterItem]);
36+
getProjectsStub = sandbox.stub(OdoImpl.prototype, 'getProjects').resolves([projectItem]);
3537
sandbox.stub(OdoImpl.prototype, 'getApplications').resolves([appItem]);
3638
sandbox.stub(OpenShiftItem, 'getApplicationNames').resolves([appItem]);
3739
sandbox.stub(vscode.window, 'showInputBox');
@@ -60,7 +62,7 @@ suite('OpenShift/Application', () => {
6062
suite('called from command palette', () => {
6163

6264
test('calls the appropriate error message when no project found', async () => {
63-
sandbox.stub(OdoImpl.prototype, 'getProjects').resolves([]);
65+
getProjectsStub.onFirstCall().resolves([]);
6466
try {
6567
await Application.describe(null);
6668
} catch (err) {
@@ -72,7 +74,6 @@ suite('OpenShift/Application', () => {
7274
});
7375

7476
test('asks to select a project and an application', async () => {
75-
sandbox.stub(OdoImpl.prototype, 'getProjects').resolves([projectItem]);
7677
const apps = [appItem];
7778
quickPickStub = sandbox.stub(vscode.window, 'showQuickPick');
7879
quickPickStub.onFirstCall().resolves(appItem);
@@ -83,7 +84,6 @@ suite('OpenShift/Application', () => {
8384
});
8485

8586
test('skips odo command execution if canceled by user', async () => {
86-
sandbox.stub(OdoImpl.prototype, 'getProjects').resolves([projectItem]);
8787
quickPickStub = sandbox.stub(vscode.window, 'showQuickPick').resolves(null);
8888
await Application.describe(null);
8989
expect(termStub).not.called;
@@ -96,16 +96,17 @@ suite('OpenShift/Application', () => {
9696

9797
setup(() => {
9898
warnStub = sandbox.stub(vscode.window, 'showWarningMessage');
99-
sandbox.stub(OdoImpl.prototype, 'getProjects').resolves([projectItem]);
100-
sandbox.stub(OdoImpl.prototype, 'getComponents').resolves([compItem]);
99+
getProjectsStub.onFirstCall().resolves([projectItem]);
100+
const pushedCompItem = new TestItem(appItem, 'app', ContextType.COMPONENT_PUSHED, []);
101+
sandbox.stub(OdoImpl.prototype, 'getApplicationChildren').resolves([pushedCompItem]);
101102
});
102103

103104
test('calls the appropriate odo command if confirmed', async () => {
104105
warnStub.resolves('Yes');
105106

106107
await Application.del(appItem);
107108

108-
expect(execStub).calledOnceWith(Command.deleteApplication(projectItem.getName(), appItem.getName()));
109+
expect(execStub).calledWith(Command.deleteApplication(projectItem.getName(), appItem.getName()));
109110
});
110111

111112
test('returns a confirmation message text when successful', async () => {

0 commit comments

Comments
 (0)