clan-app: Propagate op_key to callApi callers.
This commit is contained in:
@@ -88,6 +88,20 @@ const handleCancel = async <K extends OperationNames>(
|
||||
) => {
|
||||
console.log("Canceling operation: ", ops_key);
|
||||
const { promise, op_key } = _callApi("cancel_task", { task_id: ops_key });
|
||||
promise.catch((error) => {
|
||||
toast.custom(
|
||||
(t) => (
|
||||
<ErrorToastComponent
|
||||
t={t}
|
||||
message={"Unexpected error: " + (error?.message || String(error))}
|
||||
/>
|
||||
),
|
||||
{
|
||||
duration: 5000,
|
||||
},
|
||||
);
|
||||
console.error("Unhandled promise rejection in callApi:", error);
|
||||
});
|
||||
const resp = await promise;
|
||||
|
||||
if (resp.status === "error") {
|
||||
@@ -109,13 +123,27 @@ const handleCancel = async <K extends OperationNames>(
|
||||
console.log("Cancel response: ", resp);
|
||||
};
|
||||
|
||||
export const callApi = async <K extends OperationNames>(
|
||||
export const callApi = <K extends OperationNames>(
|
||||
method: K,
|
||||
args: OperationArgs<K>,
|
||||
): Promise<OperationResponse<K>> => {
|
||||
): { promise: Promise<OperationResponse<K>>; op_key: string } => {
|
||||
console.log("Calling API", method, args);
|
||||
|
||||
const { promise, op_key } = _callApi(method, args);
|
||||
promise.catch((error) => {
|
||||
toast.custom(
|
||||
(t) => (
|
||||
<ErrorToastComponent
|
||||
t={t}
|
||||
message={"Unexpected error: " + (error?.message || String(error))}
|
||||
/>
|
||||
),
|
||||
{
|
||||
duration: 5000,
|
||||
},
|
||||
);
|
||||
console.error("Unhandled promise rejection in callApi:", error);
|
||||
});
|
||||
|
||||
const toastId = toast.custom(
|
||||
(
|
||||
@@ -132,28 +160,30 @@ export const callApi = async <K extends OperationNames>(
|
||||
},
|
||||
);
|
||||
|
||||
const response = await promise;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cancelled = (promise as any).cancelled;
|
||||
if (cancelled) {
|
||||
console.log("Not printing toast because operation was cancelled");
|
||||
}
|
||||
const new_promise = promise.then((response) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const cancelled = (promise as any).cancelled;
|
||||
if (cancelled) {
|
||||
console.log("Not printing toast because operation was cancelled");
|
||||
}
|
||||
|
||||
if (response.status === "error" && !cancelled) {
|
||||
toast.remove(toastId);
|
||||
toast.custom(
|
||||
(t) => (
|
||||
<ErrorToastComponent
|
||||
t={t}
|
||||
message={"Error: " + response.errors[0].message}
|
||||
/>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.remove(toastId);
|
||||
}
|
||||
return response as OperationResponse<K>;
|
||||
if (response.status === "error" && !cancelled) {
|
||||
toast.remove(toastId);
|
||||
toast.custom(
|
||||
(t) => (
|
||||
<ErrorToastComponent
|
||||
t={t}
|
||||
message={"Error: " + response.errors[0].message}
|
||||
/>
|
||||
),
|
||||
{
|
||||
duration: Infinity,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.remove(toastId);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
return { promise: new_promise, op_key: op_key };
|
||||
};
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export interface Machine {
|
||||
machine: {
|
||||
name: string;
|
||||
flake: {
|
||||
identifier: string;
|
||||
};
|
||||
override_target_host: string | null;
|
||||
private_key: string | null;
|
||||
};
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export const instance_name = (machine_name: string) =>
|
||||
export async function get_iwd_service(base_path: string, machine_name: string) {
|
||||
const r = await callApi("get_inventory", {
|
||||
flake: { identifier: base_path },
|
||||
});
|
||||
}).promise;
|
||||
if (r.status == "error") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -142,11 +142,25 @@ export const ApiTester = () => {
|
||||
</Field>
|
||||
<Field name="payload">
|
||||
{(field, fieldProps) => (
|
||||
<TextInput
|
||||
label={"payload"}
|
||||
value={field.value || ""}
|
||||
inputProps={fieldProps}
|
||||
/>
|
||||
<div class="flex flex-col my-2">
|
||||
<label class="mb-1 font-medium" for="payload-textarea">
|
||||
payload
|
||||
</label>
|
||||
<textarea
|
||||
id="payload-textarea"
|
||||
class="border rounded p-2 text-sm min-h-[120px] resize-y focus:outline-none focus:ring-2 focus:ring-blue-400"
|
||||
placeholder={`{\n "key": "value"\n}`}
|
||||
value={field.value || ""}
|
||||
{...fieldProps}
|
||||
onInput={(e) => {
|
||||
fieldProps.onInput?.(e);
|
||||
}}
|
||||
spellcheck={false}
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
<Button class="m-2" disabled={query.isFetching}>
|
||||
|
||||
@@ -72,7 +72,7 @@ export const FileSelectorField: Component<FileSelectorOpts<string>> = (
|
||||
filters: fileDialogOptions.filters,
|
||||
initial_folder: fileDialogOptions.initial_folder,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
|
||||
if (
|
||||
response.status === "success" &&
|
||||
|
||||
@@ -62,7 +62,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
nix_options: [],
|
||||
password: null,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
setInstalling(false);
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
},
|
||||
override_target_host: info?.deploy.targetHost,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
|
||||
setUpdating(false);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ export const registerClan = async () => {
|
||||
try {
|
||||
const loc = await callApi("open_file", {
|
||||
file_request: { mode: "select_folder" },
|
||||
});
|
||||
}).promise;
|
||||
if (loc.status === "success" && loc.data) {
|
||||
const data = loc.data[0];
|
||||
addClanURI(data);
|
||||
@@ -32,7 +32,7 @@ export const selectSshKeys = async (): Promise<FileList> => {
|
||||
mode: "open_file",
|
||||
initial_folder: "~/.ssh",
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
if (response.status === "success" && response.data) {
|
||||
// Add synthetic files to the DataTransfer object
|
||||
// FileList cannot be instantiated directly.
|
||||
|
||||
@@ -15,7 +15,7 @@ export const clanMetaQuery = (uri: string | undefined = undefined) =>
|
||||
|
||||
const result = await callApi("show_clan_meta", {
|
||||
flake: { identifier: clanURI! },
|
||||
});
|
||||
}).promise;
|
||||
|
||||
console.log("result", result);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { useQuery } from "@tanstack/solid-query";
|
||||
import { callApi } from "../api";
|
||||
import toast from "solid-toast";
|
||||
|
||||
@@ -9,7 +9,7 @@ export const createModulesQuery = (
|
||||
uri: string | undefined,
|
||||
filter?: ModulesFilter,
|
||||
) =>
|
||||
createQuery(() => ({
|
||||
useQuery(() => ({
|
||||
queryKey: [uri, "list_modules"],
|
||||
placeholderData: {
|
||||
localModules: {},
|
||||
@@ -20,7 +20,7 @@ export const createModulesQuery = (
|
||||
if (uri) {
|
||||
const response = await callApi("list_modules", {
|
||||
base_path: uri,
|
||||
});
|
||||
}).promise;
|
||||
if (response.status === "error") {
|
||||
console.error("Failed to fetch data");
|
||||
} else {
|
||||
@@ -35,7 +35,7 @@ export const createModulesQuery = (
|
||||
}));
|
||||
|
||||
export const tagsQuery = (uri: string | undefined) =>
|
||||
createQuery<string[]>(() => ({
|
||||
useQuery<string[]>(() => ({
|
||||
queryKey: [uri, "tags"],
|
||||
placeholderData: [],
|
||||
queryFn: async () => {
|
||||
@@ -43,7 +43,7 @@ export const tagsQuery = (uri: string | undefined) =>
|
||||
|
||||
const response = await callApi("get_inventory", {
|
||||
flake: { identifier: uri },
|
||||
});
|
||||
}).promise;
|
||||
if (response.status === "error") {
|
||||
console.error("Failed to fetch data");
|
||||
} else {
|
||||
@@ -56,7 +56,7 @@ export const tagsQuery = (uri: string | undefined) =>
|
||||
}));
|
||||
|
||||
export const machinesQuery = (uri: string | undefined) =>
|
||||
createQuery<string[]>(() => ({
|
||||
useQuery<string[]>(() => ({
|
||||
queryKey: [uri, "machines"],
|
||||
placeholderData: [],
|
||||
queryFn: async () => {
|
||||
@@ -64,7 +64,7 @@ export const machinesQuery = (uri: string | undefined) =>
|
||||
|
||||
const response = await callApi("get_inventory", {
|
||||
flake: { identifier: uri },
|
||||
});
|
||||
}).promise;
|
||||
if (response.status === "error") {
|
||||
console.error("Failed to fetch data");
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,7 @@ export const CreateClan = () => {
|
||||
const { template, ...meta } = values;
|
||||
const response = await callApi("open_file", {
|
||||
file_request: { mode: "save" },
|
||||
});
|
||||
}).promise;
|
||||
|
||||
if (response.status !== "success") {
|
||||
toast.error("Cannot select clan directory");
|
||||
@@ -56,7 +56,7 @@ export const CreateClan = () => {
|
||||
machines: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
toast.dismiss(loading_toast);
|
||||
|
||||
if (r.status === "error") {
|
||||
@@ -67,7 +67,7 @@ export const CreateClan = () => {
|
||||
// Will generate a key if it doesn't exist, and add a user to the clan
|
||||
const k = await callApi("keygen", {
|
||||
flake_dir: target_dir[0],
|
||||
});
|
||||
}).promise;
|
||||
|
||||
if (k.status === "error") {
|
||||
toast.error("Failed to generate key");
|
||||
|
||||
@@ -37,7 +37,7 @@ const EditClanForm = (props: EditClanFormProps) => {
|
||||
flake: { identifier: props.directory },
|
||||
meta: values,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
})(),
|
||||
{
|
||||
loading: "Updating clan...",
|
||||
|
||||
@@ -13,7 +13,7 @@ export function DiskView() {
|
||||
// Example of calling an API
|
||||
const result = await callApi("get_inventory", {
|
||||
flake: { identifier: currUri },
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export const Flash = () => {
|
||||
const deviceQuery = createQuery(() => ({
|
||||
queryKey: ["block_devices"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("show_block_devices", {});
|
||||
const result = await callApi("show_block_devices", {}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
@@ -108,7 +108,7 @@ export const Flash = () => {
|
||||
const keymapQuery = createQuery(() => ({
|
||||
queryKey: ["list_keymaps"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("list_possible_keymaps", {});
|
||||
const result = await callApi("list_possible_keymaps", {}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
@@ -118,7 +118,7 @@ export const Flash = () => {
|
||||
const langQuery = createQuery(() => ({
|
||||
queryKey: ["list_languages"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("list_possible_languages", {});
|
||||
const result = await callApi("list_possible_languages", {}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
@@ -174,7 +174,7 @@ export const Flash = () => {
|
||||
write_efi_boot_entries: false,
|
||||
debug: false,
|
||||
graphical: true,
|
||||
}),
|
||||
}).promise,
|
||||
{
|
||||
error: (errors) => `Error flashing disk: ${errors}`,
|
||||
loading: "Flashing ... This may take up to 15minutes.",
|
||||
|
||||
@@ -56,7 +56,7 @@ export function CreateMachine() {
|
||||
identifier: active_dir,
|
||||
},
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
|
||||
if (response.status === "success") {
|
||||
toast.success(`Successfully created ${values.opts.machine.name}`);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
setValue,
|
||||
} from "@modular-forms/solid";
|
||||
import { useNavigate, useParams, useSearchParams } from "@solidjs/router";
|
||||
import { createQuery, useQuery, useQueryClient } from "@tanstack/solid-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/solid-query";
|
||||
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
|
||||
import { Button } from "../../components/Button/Button";
|
||||
@@ -130,7 +130,7 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
placeholders: diskValues.placeholders,
|
||||
schema_name: diskValues.schema,
|
||||
force: true,
|
||||
});
|
||||
}).promise;
|
||||
}
|
||||
|
||||
setProgressText("Installing machine ... (2/5)");
|
||||
@@ -425,7 +425,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
values.machine.tags || props.initialData.machine.tags || [],
|
||||
),
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [curr_uri, "machine", machineName(), "get_machine_details"],
|
||||
});
|
||||
@@ -433,7 +433,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const generatorsQuery = createQuery(() => ({
|
||||
const generatorsQuery = useQuery(() => ({
|
||||
queryKey: [activeClanURI(), machineName(), "generators"],
|
||||
queryFn: async () => {
|
||||
const machine_name = machineName();
|
||||
@@ -444,7 +444,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
const result = await callApi("get_generators_closure", {
|
||||
base_dir: base_dir,
|
||||
machine_name: machine_name,
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
@@ -489,7 +489,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
},
|
||||
override_target_host: target,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
@@ -717,7 +717,7 @@ export const MachineDetails = () => {
|
||||
},
|
||||
name: params.id,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const DiskStep = (props: StepProps<DiskValues>) => {
|
||||
},
|
||||
name: props.machine_id,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ export const HWStep = (props: StepProps<HardwareValues>) => {
|
||||
},
|
||||
name: props.machine_id,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
|
||||
@@ -153,7 +153,7 @@ export const VarsStep = (props: VarsStepProps) => {
|
||||
base_dir: props.dir,
|
||||
machine_name: props.machine_id,
|
||||
full_closure: props.fullClosure,
|
||||
});
|
||||
}).promise;
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
@@ -170,7 +170,7 @@ export const VarsStep = (props: VarsStepProps) => {
|
||||
base_dir: props.dir,
|
||||
generators: generatorsQuery.data.map((generator) => generator.name),
|
||||
all_prompt_values: values,
|
||||
});
|
||||
}).promise;
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [props.dir, props.machine_id, "generators"],
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ export const MachineListView: Component = () => {
|
||||
flake: {
|
||||
identifier: uri,
|
||||
},
|
||||
});
|
||||
}).promise;
|
||||
console.log("response", response);
|
||||
if (response.status === "error") {
|
||||
console.error("Failed to fetch data");
|
||||
|
||||
Reference in New Issue
Block a user