mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 00:07:50 +00:00
fix(plugins): list the marketplace and install plugins from the welcome screen
The Plugins page on the welcome screen showed "[object Object]" and "There is no online plugin repository" (even with a marketplace configured), and Install did nothing. - Marketplace and package commands (index, download, unpack, install preview, theme import) only touch the IDE's own state and are allowed without a trusted project. - Installing, uninstalling, enabling and granting plugins are allowed where no project is open; an untrusted project still blocks them. - Errors rejected as plain backend error objects show their message, and a marketplace that couldn't be read shows the error instead of the "no online repository" note.
This commit is contained in:
@@ -274,6 +274,15 @@ pub fn restricted_command_allowed(cmd: &str) -> bool {
|
||||
| "plugin_set_theme_frame"
|
||||
| "plugin_catalog"
|
||||
| "plugin_assets"
|
||||
| "plugin_market"
|
||||
| "plugin_market_url"
|
||||
| "plugin_set_market_url"
|
||||
| "plugin_market_download"
|
||||
| "plugin_unpack_package"
|
||||
| "plugin_stage_package"
|
||||
| "plugin_preview_install"
|
||||
| "plugin_read_intellij_theme"
|
||||
| "plugin_installed_intellij_themes"
|
||||
| "notebook_open"
|
||||
| "notebook_get"
|
||||
| "notebook_edit"
|
||||
@@ -319,12 +328,31 @@ pub fn restricted_command_allowed(cmd: &str) -> bool {
|
||||
) || cmd.starts_with("plugin:")
|
||||
}
|
||||
|
||||
/// Commands that change which plugin code runs: allowed where no project is open (the
|
||||
/// welcome screen), otherwise only in a trusted project.
|
||||
pub fn projectless_command_allowed(cmd: &str) -> bool {
|
||||
matches!(
|
||||
cmd,
|
||||
"plugin_install_dir"
|
||||
| "plugin_uninstall"
|
||||
| "plugin_set_enabled"
|
||||
| "plugin_grant"
|
||||
| "plugin_set_capability"
|
||||
| "plugin_roll_back"
|
||||
| "plugin_reload"
|
||||
)
|
||||
}
|
||||
|
||||
impl crate::Host {
|
||||
pub fn authorize_command(&self, window: &str, command: &str) -> IdeResult<()> {
|
||||
if restricted_command_allowed(command) {
|
||||
return Ok(());
|
||||
}
|
||||
self.project(window)?.require_trust()
|
||||
match self.project(window) {
|
||||
Ok(project) => project.require_trust(),
|
||||
Err(_) if projectless_command_allowed(command) => Ok(()),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,11 @@ async fn application_commands_work_in_restricted_projects_and_without_a_project(
|
||||
"vcs_write_patch",
|
||||
"remote_bootstrap_list",
|
||||
"deploy_test_connection",
|
||||
"plugin_market",
|
||||
"plugin_market_url",
|
||||
"plugin_set_market_url",
|
||||
"plugin_market_download",
|
||||
"plugin_preview_install",
|
||||
] {
|
||||
assert!(ide_host::trust::restricted_command_allowed(command), "{command}");
|
||||
for window in ["main", "welcome"] {
|
||||
@@ -72,8 +77,6 @@ async fn application_commands_work_in_restricted_projects_and_without_a_project(
|
||||
"tools_list",
|
||||
"tools_install",
|
||||
"tools_uninstall",
|
||||
"plugin_install_dir",
|
||||
"plugin_reload",
|
||||
"vcs_git_version",
|
||||
"docker_compose_op",
|
||||
"db_execute",
|
||||
@@ -93,6 +96,12 @@ async fn application_commands_work_in_restricted_projects_and_without_a_project(
|
||||
assert!(host.authorize_command(window, command).is_err(), "{window}: {command}");
|
||||
}
|
||||
}
|
||||
// Plugin installs run plugin code: from the welcome screen, but not in an untrusted project.
|
||||
for command in ["plugin_install_dir", "plugin_uninstall", "plugin_set_enabled", "plugin_reload"] {
|
||||
assert!(!ide_host::trust::restricted_command_allowed(command), "{command}");
|
||||
assert!(host.authorize_command("main", command).is_err(), "{command}");
|
||||
assert!(host.authorize_command("welcome", command).is_ok(), "{command}");
|
||||
}
|
||||
// These requests validate the vendor before any network access, independently of project trust.
|
||||
for window in ["main", "welcome"] {
|
||||
let error = ide_host::ops::tools::jdk_download_releases(
|
||||
|
||||
@@ -924,7 +924,7 @@ export function PluginsPage() {
|
||||
<CategoryFilter market={market ?? []} value={category} onChange={setCategory} />
|
||||
</li>
|
||||
)}
|
||||
{!marketUrl && (
|
||||
{!marketUrl && !marketError && (
|
||||
<li className={styles.notice} role="note">
|
||||
<Icon name="info" size={14} />
|
||||
<span>{tx`${NO_REPOSITORY}. Showing plugin packages available on this machine.`}</span>
|
||||
|
||||
@@ -570,6 +570,21 @@ describe("PluginsPage", () => {
|
||||
expect(within(list).getAllByText("Installed").length).toBeGreaterThan(0);
|
||||
expect(notes.at(-1)).toMatchObject({ title: "Installed Toy Language 0.1.0" });
|
||||
});
|
||||
|
||||
it("shows why the marketplace couldn't be read instead of claiming there is none", async () => {
|
||||
// Commands without a result type reject with the backend's error object, not an Error.
|
||||
setPluginsBackend({
|
||||
...createMemoryPluginsBackend([], sampleCatalog()),
|
||||
marketUrl: () => Promise.reject({ kind: "unavailable", message: "Index unreachable", data: null }),
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
render(<PluginsPage />);
|
||||
await user.click(screen.getByRole("tab", { name: /Available/ }));
|
||||
const list = await screen.findByRole("list", { name: "Available plugins" });
|
||||
expect(await within(list).findByText("Index unreachable")).toBeInTheDocument();
|
||||
expect(within(list).queryByText("[object Object]")).not.toBeInTheDocument();
|
||||
expect(within(list).queryByText(new RegExp(NO_REPOSITORY))).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("secrets", () => {
|
||||
|
||||
@@ -54,7 +54,13 @@ export const usePlugins = create<PluginsState>(() => ({
|
||||
marketError: null,
|
||||
}));
|
||||
|
||||
export const errorText = (e: unknown) => (e instanceof Error ? e.message : String(e));
|
||||
/** A rejection's message: an `Error`, or an `IdeError` object from a command without a result type. */
|
||||
export const errorText = (e: unknown): string =>
|
||||
e instanceof Error
|
||||
? e.message
|
||||
: typeof e === "object" && e !== null && "message" in e && typeof e.message === "string"
|
||||
? e.message
|
||||
: String(e);
|
||||
|
||||
/** Statuses whose contributions are in effect. */
|
||||
export const isLive = (status: PluginStatus) => status === "active" || status === "inactive" || status === "static";
|
||||
|
||||
Reference in New Issue
Block a user