Skip to content

Commit 1199526

Browse files
authored
chore: using simple objects in rpc api (to avoid snapshots) (#1314)
* chore: using simple objects in rpc api (to avoid snapshots) Moving to svelte 5, state objects are proxied. Normally that's an implementation detail, but when you try to pass the proxies through rpc they can't be cloned and result in errors. We could use $state.snapshot(build) every time we pass objects from the UI to the API, but we really shouldn't be passing whole objects to the backend and blindly trusting that the objects (including all properties) match what the backend knows anyway. This changes the three affected API functions to accept ids instead of objects, and the backend looks up the objects before acting. Although it's slightly more work on the backend, this is a better/more robust API, and also avoids the svelte 5 proxy issue. Fixes #1304. Signed-off-by: Tim deBoer <[email protected]> * chore: correct throw error, remove console.log Signed-off-by: Tim deBoer <[email protected]> * chore: use !! filter to improve type filtering Signed-off-by: Tim deBoer <[email protected]> --------- Signed-off-by: Tim deBoer <[email protected]>
1 parent 64501ae commit 1199526

File tree

8 files changed

+31
-13
lines changed

8 files changed

+31
-13
lines changed

packages/backend/src/api-impl.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ export class BootcApiImpl implements BootcApi {
5454
return checkPrereqs(await getContainerEngine());
5555
}
5656

57-
async checkVMLaunchPrereqs(build: BootcBuildInfo): Promise<string | undefined> {
57+
async checkVMLaunchPrereqs(buildId: string): Promise<string | undefined> {
58+
const build = this.history.getHistory().find(build => build.id === buildId);
59+
if (!build) {
60+
throw new Error(`Could not find build: ${buildId}`);
61+
}
5862
return createVMManager(build).checkVMLaunchPrereqs();
5963
}
6064

@@ -66,11 +70,16 @@ export class BootcApiImpl implements BootcApi {
6670
return buildDiskImage(build, this.history, overwrite);
6771
}
6872

69-
async launchVM(build: BootcBuildInfo): Promise<void> {
73+
async launchVM(buildId: string): Promise<void> {
7074
try {
71-
await createVMManager(build).launchVM();
72-
// Notify it has successfully launched
73-
await this.notify(Messages.MSG_VM_LAUNCH_ERROR, { success: 'Launched!', error: '' });
75+
const build = this.history.getHistory().find(build => build.id === buildId);
76+
if (!build) {
77+
await this.notify(Messages.MSG_VM_LAUNCH_ERROR, { success: '', error: 'Could not find build to launch' });
78+
} else {
79+
await createVMManager(build).launchVM();
80+
// Notify it has successfully launched
81+
await this.notify(Messages.MSG_VM_LAUNCH_ERROR, { success: 'Launched!', error: '' });
82+
}
7483
} catch (e) {
7584
// Make sure that we are able to display the "stderr" information if it exists as that actually shows
7685
// the error when running the command.
@@ -90,13 +99,18 @@ export class BootcApiImpl implements BootcApi {
9099
return stopCurrentVM();
91100
}
92101

93-
async deleteBuilds(builds: BootcBuildInfo[]): Promise<void> {
102+
async deleteBuilds(buildIds: string[]): Promise<void> {
94103
const response = await podmanDesktopApi.window.showWarningMessage(
95104
`Are you sure you want to remove the selected disk images from the build history? This will remove the history of the build as well as remove any lingering build containers.`,
96105
'Yes',
97106
'No',
98107
);
99108
if (response === 'Yes') {
109+
// create an array of builds. invalid build ids are ignored
110+
const builds = buildIds
111+
.map(id => this.history.getHistory().find(build => build.id === id))
112+
.filter(build => !!build);
113+
100114
// Map each build to a delete operation promise
101115
const deletePromises = builds.map(build => this.deleteBuildContainer(build));
102116

packages/frontend/src/lib/disk-image/DiskImageActions.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ test('Test clicking on delete button', async () => {
7373
deleteButton.click();
7474

7575
expect(spyOnDelete).toHaveBeenCalled();
76+
expect(spyOnDelete).toHaveBeenCalledWith(['name1']);
7677
});
7778

7879
test('Test clicking on logs button', async () => {

packages/frontend/src/lib/disk-image/DiskImageActions.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let isWindows = $state(false);
1616
1717
// Delete the build
1818
async function deleteBuild(): Promise<void> {
19-
await bootcClient.deleteBuilds([object]);
19+
await bootcClient.deleteBuilds([object.id]);
2020
}
2121
2222
// Navigate to the build

packages/frontend/src/lib/disk-image/DiskImageDetailsVirtualMachine.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ test('Render virtual machine terminal window', async () => {
6969
await waitFor(() => {
7070
expect(bootcClient.launchVM).toHaveBeenCalled();
7171
});
72+
expect(bootcClient.checkVMLaunchPrereqs).toHaveBeenCalledWith('id1');
73+
expect(bootcClient.launchVM).toHaveBeenCalledWith('id1');
7274
});
7375

7476
test('Show prereqs message if prereq check fails (returns ANY string)', async () => {

packages/frontend/src/lib/disk-image/DiskImageDetailsVirtualMachine.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ async function launchVM(build: BootcBuildInfo): Promise<void> {
185185
186186
// This is launched IN THE BACKGROUND. We do not wait for the VM to boot before showing the terminal.
187187
// we instead are notified by subscribing to Messages.MSG_VM_LAUNCH_ERROR messages from RPC
188-
bootcClient.launchVM(build).catch((e: unknown) => console.error('error launching VM', e));
188+
bootcClient.launchVM(build.id).catch((e: unknown) => console.error('error launching VM', e));
189189
190190
// Initialize the terminal so it awaits the websocket connection.
191191
await initTerminal();
@@ -261,7 +261,7 @@ onMount(async () => {
261261
262262
if (build) {
263263
// Launch the VM if we pass all the prerequisites, otherwise we will show the empty screen with content / information checks.
264-
vmLaunchPrereqs = await bootcClient.checkVMLaunchPrereqs(build);
264+
vmLaunchPrereqs = await bootcClient.checkVMLaunchPrereqs(build.id);
265265
266266
if (!vmLaunchPrereqs) {
267267
await launchVM(build);

packages/frontend/src/lib/disk-image/DiskImagesList.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ test('Test clicking on delete button', async () => {
113113
deleteButton.click();
114114

115115
expect(spyOnDelete).toHaveBeenCalled();
116+
expect(spyOnDelete).toHaveBeenCalledWith(['name1']);
116117
});
117118

118119
test('Test clicking on build button', async () => {

packages/frontend/src/lib/disk-image/DiskImagesList.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ onMount(() => {
4646
let bulkDeleteInProgress = $state(false);
4747
4848
async function deleteSelectedBuilds(): Promise<void> {
49-
const selected = history.filter(history => history.selected);
49+
const selected = history.filter(history => history.selected).map(history => history.id);
5050
if (selected.length === 0) {
5151
return;
5252
}

packages/shared/src/BootcAPI.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ import type { ExamplesList } from './models/examples';
2323
export abstract class BootcApi {
2424
static readonly CHANNEL: string = 'BootcApi';
2525
abstract checkPrereqs(): Promise<string | undefined>;
26-
abstract checkVMLaunchPrereqs(build: BootcBuildInfo): Promise<string | undefined>;
27-
abstract launchVM(build: BootcBuildInfo): Promise<void>;
26+
abstract checkVMLaunchPrereqs(buildId: string): Promise<string | undefined>;
27+
abstract launchVM(buildId: string): Promise<void>;
2828
abstract buildExists(folder: string, types: BuildType[]): Promise<boolean>;
2929
abstract buildImage(build: BootcBuildInfo, overwrite?: boolean): Promise<void>;
3030
abstract pullImage(image: string, arch?: string): Promise<void>;
3131
abstract inspectImage(image: ImageInfo): Promise<ImageInspectInfo>;
3232
abstract inspectManifest(image: ImageInfo): Promise<ManifestInspectInfo>;
33-
abstract deleteBuilds(builds: BootcBuildInfo[]): Promise<void>;
33+
abstract deleteBuilds(buildIds: string[]): Promise<void>;
3434
abstract selectOutputFolder(): Promise<string>;
3535
abstract selectBuildConfigFile(): Promise<string>;
3636
abstract listBootcImages(): Promise<ImageInfo[]>;

0 commit comments

Comments
 (0)