refactor: remove Tool Configs and Config Folders
These features are fully superseded by Config Profiles which provide: - Env vars, file mounts, port overrides, start commands, working dirs - Git mounts, profile composition, cycle detection - Default selection, project/tool-type scoping Changes: - Delete backend models: ToolConfig, ConfigFolder - Delete backend APIs: tool_configs.py, config_folders.py - Delete frontend API clients: tool_configs.ts, config_folders.ts - Remove Tool Config fetching from start_instance, use ConfigProfile only - Simplify merge_with_config to accept only profile (no tool_configs) - Remove configs/folders tabs from Tool Workshop page - Delete associated integration and unit tests - Add Alembic migration to drop tool_configs and config_folders tables Quality gates: backend tests 59 passed, frontend typecheck clean
This commit is contained in:
@@ -1,131 +0,0 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
createConfigFolder,
|
||||
deleteConfigFolder,
|
||||
listConfigFolders,
|
||||
updateConfigFolder,
|
||||
} from "../api/config_folders";
|
||||
|
||||
const mockGet = vi.fn();
|
||||
const mockPost = vi.fn();
|
||||
const mockPut = vi.fn();
|
||||
const mockDelete = vi.fn();
|
||||
|
||||
vi.mock("../api/client", () => ({
|
||||
apiClient: {
|
||||
get: (...args: unknown[]) => mockGet(...args),
|
||||
post: (...args: unknown[]) => mockPost(...args),
|
||||
put: (...args: unknown[]) => mockPut(...args),
|
||||
delete: (...args: unknown[]) => mockDelete(...args),
|
||||
interceptors: {
|
||||
response: {
|
||||
use: vi.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldSkipAuthRedirect: vi.fn(() => false),
|
||||
}));
|
||||
|
||||
describe("config_folders API", () => {
|
||||
describe("listConfigFolders", () => {
|
||||
it("returns folders with files and overrides", async () => {
|
||||
const mockResponse = {
|
||||
data: [
|
||||
{
|
||||
id: "folder-1",
|
||||
name: "my-dotfiles",
|
||||
description: "My personal config files",
|
||||
mount_path: "/home/user",
|
||||
files: { ".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"" },
|
||||
project_overrides: {},
|
||||
is_active: true,
|
||||
user_id: "user-1",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
};
|
||||
mockGet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await listConfigFolders();
|
||||
|
||||
expect(result[0].name).toBe("my-dotfiles");
|
||||
expect(result[0].files).toEqual({ ".zshrc": "export ZSH=\"$HOME/.oh-my-zsh\"" });
|
||||
expect(mockGet).toHaveBeenCalledWith("/config-folders");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createConfigFolder", () => {
|
||||
it("creates folder with files", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
id: "folder-new",
|
||||
name: "new-folder",
|
||||
mount_path: "/workspace",
|
||||
files: { ".env": "API_URL=http://localhost" },
|
||||
is_active: true,
|
||||
user_id: "user-1",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
};
|
||||
mockPost.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await createConfigFolder({
|
||||
name: "new-folder",
|
||||
mount_path: "/workspace",
|
||||
files: { ".env": "API_URL=http://localhost" },
|
||||
});
|
||||
|
||||
expect(result.name).toBe("new-folder");
|
||||
expect(mockPost).toHaveBeenCalledWith(
|
||||
"/config-folders",
|
||||
expect.objectContaining({
|
||||
name: "new-folder",
|
||||
mount_path: "/workspace",
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("updateConfigFolder", () => {
|
||||
it("updates folder files", async () => {
|
||||
const mockResponse = {
|
||||
data: {
|
||||
id: "folder-1",
|
||||
name: "updated-folder",
|
||||
mount_path: "/home/user",
|
||||
files: { ".bashrc": "alias ll='ls -la'" },
|
||||
is_active: true,
|
||||
user_id: "user-1",
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
};
|
||||
mockPut.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await updateConfigFolder("folder-1", {
|
||||
files: { ".bashrc": "alias ll='ls -la'" },
|
||||
});
|
||||
|
||||
expect(result.files).toEqual({ ".bashrc": "alias ll='ls -la'" });
|
||||
expect(mockPut).toHaveBeenCalledWith(
|
||||
"/config-folders/folder-1",
|
||||
expect.objectContaining({
|
||||
files: { ".bashrc": "alias ll='ls -la'" },
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteConfigFolder", () => {
|
||||
it("deletes folder", async () => {
|
||||
mockDelete.mockResolvedValue({ data: undefined });
|
||||
|
||||
await deleteConfigFolder("folder-1");
|
||||
|
||||
expect(mockDelete).toHaveBeenCalledWith("/config-folders/folder-1");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,95 +0,0 @@
|
||||
import { apiClient } from "./client";
|
||||
|
||||
export interface ConfigFolder {
|
||||
id: string;
|
||||
user_id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
mount_path: string;
|
||||
files: Record<string, string>;
|
||||
project_overrides: Record<string, { mount_path?: string; files?: Record<string, string> }> | null;
|
||||
is_active: boolean;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CreateConfigFolderRequest {
|
||||
name: string;
|
||||
description?: string;
|
||||
mount_path: string;
|
||||
files?: Record<string, string>;
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateConfigFolderRequest {
|
||||
name?: string;
|
||||
description?: string;
|
||||
mount_path?: string;
|
||||
files?: Record<string, string>;
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
export interface ProjectOverrideRequest {
|
||||
mount_path?: string;
|
||||
files?: Record<string, string>;
|
||||
}
|
||||
|
||||
export const listConfigFolders = async (): Promise<ConfigFolder[]> => {
|
||||
const response = await apiClient.get<ConfigFolder[]>("/config-folders");
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getConfigFolder = async (id: string): Promise<ConfigFolder> => {
|
||||
const response = await apiClient.get<ConfigFolder>(`/config-folders/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const createConfigFolder = async (
|
||||
data: CreateConfigFolderRequest
|
||||
): Promise<ConfigFolder> => {
|
||||
const response = await apiClient.post<ConfigFolder>("/config-folders", data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateConfigFolder = async (
|
||||
id: string,
|
||||
data: UpdateConfigFolderRequest
|
||||
): Promise<ConfigFolder> => {
|
||||
const response = await apiClient.put<ConfigFolder>(`/config-folders/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const deleteConfigFolder = async (id: string): Promise<void> => {
|
||||
await apiClient.delete(`/config-folders/${id}`);
|
||||
};
|
||||
|
||||
export const addProjectOverride = async (
|
||||
id: string,
|
||||
projectId: string,
|
||||
data: ProjectOverrideRequest
|
||||
): Promise<ConfigFolder> => {
|
||||
const response = await apiClient.post<ConfigFolder>(
|
||||
`/config-folders/${id}/overrides/${projectId}`,
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateProjectOverride = async (
|
||||
id: string,
|
||||
projectId: string,
|
||||
data: ProjectOverrideRequest
|
||||
): Promise<ConfigFolder> => {
|
||||
const response = await apiClient.put<ConfigFolder>(
|
||||
`/config-folders/${id}/overrides/${projectId}`,
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const deleteProjectOverride = async (
|
||||
id: string,
|
||||
projectId: string
|
||||
): Promise<void> => {
|
||||
await apiClient.delete(`/config-folders/${id}/overrides/${projectId}`);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
import { apiClient } from "./client";
|
||||
|
||||
export interface ToolConfig {
|
||||
id: string;
|
||||
tool_type_id: string;
|
||||
project_id: string | null;
|
||||
key: string;
|
||||
value: string;
|
||||
config_type: string;
|
||||
file_path: string | null;
|
||||
port_override: number | null;
|
||||
start_command: string | null;
|
||||
working_directory: string | null;
|
||||
environment_variables: Record<string, string> | null;
|
||||
volumes: Array<{ source: string; target: string; type?: string }> | null;
|
||||
}
|
||||
|
||||
export interface CreateToolConfigRequest {
|
||||
tool_type_id: string;
|
||||
project_id?: string;
|
||||
key: string;
|
||||
value: string;
|
||||
config_type?: string;
|
||||
file_path?: string;
|
||||
port_override?: number;
|
||||
start_command?: string;
|
||||
working_directory?: string;
|
||||
environment_variables?: Record<string, string>;
|
||||
volumes?: Array<{ source: string; target: string; type?: string }>;
|
||||
}
|
||||
|
||||
export const listToolConfigs = async (
|
||||
tool_type_id?: string,
|
||||
project_id?: string
|
||||
): Promise<ToolConfig[]> => {
|
||||
const params = new URLSearchParams();
|
||||
if (tool_type_id) params.append("tool_type_id", tool_type_id);
|
||||
if (project_id) params.append("project_id", project_id);
|
||||
|
||||
const response = await apiClient.get<{ configs: ToolConfig[] }>(
|
||||
`/tool-configs?${params.toString()}`
|
||||
);
|
||||
return response.data.configs;
|
||||
};
|
||||
|
||||
export const createToolConfig = async (
|
||||
data: CreateToolConfigRequest
|
||||
): Promise<ToolConfig> => {
|
||||
const response = await apiClient.post<{ configs: ToolConfig[] }>("/tool-configs", data);
|
||||
return response.data.configs[0];
|
||||
};
|
||||
|
||||
export const updateToolConfig = async (
|
||||
id: string,
|
||||
data: CreateToolConfigRequest
|
||||
): Promise<ToolConfig> => {
|
||||
const response = await apiClient.put<{ configs: ToolConfig[] }>(
|
||||
`/tool-configs/${id}`,
|
||||
data
|
||||
);
|
||||
return response.data.configs[0];
|
||||
};
|
||||
|
||||
export const deleteToolConfig = async (id: string): Promise<void> => {
|
||||
await apiClient.delete(`/tool-configs/${id}`);
|
||||
};
|
||||
|
||||
export const getToolConfigDefaults = async (
|
||||
toolTypeId: string
|
||||
): Promise<ToolConfig> => {
|
||||
const response = await apiClient.get<ToolConfig>(
|
||||
`/tool-configs/defaults/${toolTypeId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
Reference in New Issue
Block a user