chore(lite): add type branding to XAPI record's $ref & uuid (#6884)
Type branding enhances our type safety by preventing the incorrect usage of `XenApiRecord`'s `$ref` and `uuid`. It ensures that these types are not interchangeable.
This commit is contained in:
committed by
GitHub
parent
fcc76fb8d0
commit
2de26030ff
@@ -6,23 +6,26 @@
|
||||
<slot v-else />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script
|
||||
generic="T extends XenApiRecord<string>, I extends T['uuid']"
|
||||
lang="ts"
|
||||
setup
|
||||
>
|
||||
import UiSpinner from "@/components/ui/UiSpinner.vue";
|
||||
import type { XenApiRecord } from "@/libs/xen-api";
|
||||
import ObjectNotFoundView from "@/views/ObjectNotFoundView.vue";
|
||||
import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
isReady: boolean;
|
||||
uuidChecker: (uuid: string) => boolean;
|
||||
id?: string;
|
||||
uuidChecker: (uuid: I) => boolean;
|
||||
id?: I;
|
||||
}>();
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const id = computed(
|
||||
() => props.id ?? (currentRoute.value.params.uuid as string)
|
||||
);
|
||||
const id = computed(() => props.id ?? (currentRoute.value.params.uuid as I));
|
||||
|
||||
const isRecordNotFound = computed(
|
||||
() => props.isReady && !props.uuidChecker(id.value)
|
||||
|
||||
@@ -29,6 +29,7 @@ import InfraAction from "@/components/infra/InfraAction.vue";
|
||||
import InfraItemLabel from "@/components/infra/InfraItemLabel.vue";
|
||||
import InfraVmList from "@/components/infra/InfraVmList.vue";
|
||||
import { vTooltip } from "@/directives/tooltip.directive";
|
||||
import type { XenApiHost } from "@/libs/xen-api";
|
||||
import { useHostStore } from "@/stores/host.store";
|
||||
import { usePoolStore } from "@/stores/pool.store";
|
||||
import { useUiStore } from "@/stores/ui.store";
|
||||
@@ -42,7 +43,7 @@ import { useToggle } from "@vueuse/core";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
hostOpaqueRef: string;
|
||||
hostOpaqueRef: XenApiHost["$ref"];
|
||||
}>();
|
||||
|
||||
const { getByOpaqueRef } = useHostStore().subscribe();
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
import InfraAction from "@/components/infra/InfraAction.vue";
|
||||
import InfraItemLabel from "@/components/infra/InfraItemLabel.vue";
|
||||
import PowerStateIcon from "@/components/PowerStateIcon.vue";
|
||||
import type { XenApiVm } from "@/libs/xen-api";
|
||||
import { useVmStore } from "@/stores/vm.store";
|
||||
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useIntersectionObserver } from "@vueuse/core";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
vmOpaqueRef: string;
|
||||
vmOpaqueRef: XenApiVm["$ref"];
|
||||
}>();
|
||||
|
||||
const { getByOpaqueRef } = useVmStore().subscribe();
|
||||
|
||||
@@ -11,18 +11,21 @@
|
||||
<script lang="ts" setup>
|
||||
import InfraLoadingItem from "@/components/infra/InfraLoadingItem.vue";
|
||||
import InfraVmItem from "@/components/infra/InfraVmItem.vue";
|
||||
import type { XenApiHost } from "@/libs/xen-api";
|
||||
import { useVmStore } from "@/stores/vm.store";
|
||||
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
hostOpaqueRef?: string;
|
||||
hostOpaqueRef?: XenApiHost["$ref"];
|
||||
}>();
|
||||
|
||||
const { isReady, recordsByHostRef, hasError } = useVmStore().subscribe();
|
||||
|
||||
const vms = computed(() =>
|
||||
recordsByHostRef.value.get(props.hostOpaqueRef ?? "OpaqueRef:NULL")
|
||||
recordsByHostRef.value.get(
|
||||
props.hostOpaqueRef ?? ("OpaqueRef:NULL" as XenApiHost["$ref"])
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
selectedRefs: string[];
|
||||
selectedRefs: XenApiVm["$ref"][];
|
||||
}>();
|
||||
|
||||
const { getByOpaqueRef } = useVmStore().subscribe();
|
||||
|
||||
@@ -47,7 +47,7 @@ import { vTooltip } from "@/directives/tooltip.directive";
|
||||
import type { XenApiVm } from "@/libs/xen-api";
|
||||
|
||||
const props = defineProps<{
|
||||
vmRefs: string[];
|
||||
vmRefs: XenApiVm["$ref"][];
|
||||
}>();
|
||||
|
||||
const xenApi = useXenApiStore().getXapi();
|
||||
|
||||
@@ -118,7 +118,7 @@ import {
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
vmRefs: string[];
|
||||
vmRefs: XenApiVm["$ref"][];
|
||||
}>();
|
||||
|
||||
const { getByOpaqueRef: getVm } = useVmStore().subscribe();
|
||||
|
||||
@@ -22,6 +22,7 @@ import UiIcon from "@/components/ui/icon/UiIcon.vue";
|
||||
import UiButton from "@/components/ui/UiButton.vue";
|
||||
import { useVmStore } from "@/stores/vm.store";
|
||||
import VmActionPowerStateItems from "@/components/vm/VmActionItems/VmActionPowerStateItems.vue";
|
||||
import type { XenApiVm } from "@/libs/xen-api";
|
||||
import {
|
||||
faAngleDown,
|
||||
faDisplay,
|
||||
@@ -34,7 +35,7 @@ const { getByUuid: getVmByUuid } = useVmStore().subscribe();
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const vm = computed(() =>
|
||||
getVmByUuid(currentRoute.value.params.uuid as string)
|
||||
getVmByUuid(currentRoute.value.params.uuid as XenApiVm["uuid"])
|
||||
);
|
||||
|
||||
const name = computed(() => vm.value?.name_label);
|
||||
|
||||
@@ -56,10 +56,12 @@
|
||||
import AppMenu from "@/components/menu/AppMenu.vue";
|
||||
import MenuItem from "@/components/menu/MenuItem.vue";
|
||||
import UiButton from "@/components/ui/UiButton.vue";
|
||||
import { useUiStore } from "@/stores/ui.store";
|
||||
import VmActionPowerStateItems from "@/components/vm/VmActionItems/VmActionPowerStateItems.vue";
|
||||
import VmActionCopyItem from "@/components/vm/VmActionItems/VmActionCopyItem.vue";
|
||||
import VmActionDeleteItem from "@/components/vm/VmActionItems/VmActionDeleteItem.vue";
|
||||
import VmActionPowerStateItems from "@/components/vm/VmActionItems/VmActionPowerStateItems.vue";
|
||||
import { vTooltip } from "@/directives/tooltip.directive";
|
||||
import type { XenApiVm } from "@/libs/xen-api";
|
||||
import { useUiStore } from "@/stores/ui.store";
|
||||
import {
|
||||
faCamera,
|
||||
faCode,
|
||||
@@ -72,11 +74,10 @@ import {
|
||||
faRoute,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { vTooltip } from "@/directives/tooltip.directive";
|
||||
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
selectedRefs: string[];
|
||||
selectedRefs: XenApiVm["$ref"][];
|
||||
}>();
|
||||
|
||||
const { isMobile } = storeToRefs(useUiStore());
|
||||
|
||||
@@ -16,10 +16,13 @@ export type Stat<T> = {
|
||||
pausable: Pausable;
|
||||
};
|
||||
|
||||
type GetStats<T extends HostStats | VmStats> = (
|
||||
uuid: string,
|
||||
type GetStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats
|
||||
> = (
|
||||
uuid: T["uuid"],
|
||||
granularity: GRANULARITY
|
||||
) => Promise<XapiStatsResponse<T>> | undefined;
|
||||
) => Promise<XapiStatsResponse<S>> | undefined;
|
||||
|
||||
export type FetchedStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
@@ -35,7 +38,7 @@ export type FetchedStats<
|
||||
export default function useFetchStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats
|
||||
>(getStats: GetStats<S>, granularity: GRANULARITY): FetchedStats<T, S> {
|
||||
>(getStats: GetStats<T, S>, granularity: GRANULARITY): FetchedStats<T, S> {
|
||||
const stats = ref<Map<string, Stat<S>>>(new Map());
|
||||
const timestamp = ref<number[]>([0, 0]);
|
||||
|
||||
|
||||
@@ -136,9 +136,9 @@ export function getHostMemory(
|
||||
}
|
||||
}
|
||||
|
||||
export const buildXoObject = <T extends XenApiRecord>(
|
||||
export const buildXoObject = <T extends XenApiRecord<string>>(
|
||||
record: RawXenApiRecord<T>,
|
||||
params: { opaqueRef: string }
|
||||
params: { opaqueRef: T["$ref"] }
|
||||
) => {
|
||||
return {
|
||||
...record,
|
||||
|
||||
@@ -88,78 +88,80 @@ export enum VM_OPERATION {
|
||||
SUSPEND = "suspend",
|
||||
}
|
||||
|
||||
export interface XenApiRecord {
|
||||
$ref: string;
|
||||
uuid: string;
|
||||
declare const __brand: unique symbol;
|
||||
|
||||
export interface XenApiRecord<Name extends string> {
|
||||
$ref: string & { [__brand]: `${Name}Ref` };
|
||||
uuid: string & { [__brand]: `${Name}Uuid` };
|
||||
}
|
||||
|
||||
export type RawXenApiRecord<T extends XenApiRecord> = Omit<T, "$ref">;
|
||||
export type RawXenApiRecord<T extends XenApiRecord<string>> = Omit<T, "$ref">;
|
||||
|
||||
export interface XenApiPool extends XenApiRecord {
|
||||
export interface XenApiPool extends XenApiRecord<"Pool"> {
|
||||
cpu_info: {
|
||||
cpu_count: string;
|
||||
};
|
||||
master: string;
|
||||
master: XenApiHost["$ref"];
|
||||
name_label: string;
|
||||
}
|
||||
|
||||
export interface XenApiHost extends XenApiRecord {
|
||||
export interface XenApiHost extends XenApiRecord<"Host"> {
|
||||
address: string;
|
||||
name_label: string;
|
||||
metrics: string;
|
||||
resident_VMs: string[];
|
||||
metrics: XenApiHostMetrics["$ref"];
|
||||
resident_VMs: XenApiVm["$ref"][];
|
||||
cpu_info: { cpu_count: string };
|
||||
software_version: { product_version: string };
|
||||
}
|
||||
|
||||
export interface XenApiSr extends XenApiRecord {
|
||||
export interface XenApiSr extends XenApiRecord<"Sr"> {
|
||||
name_label: string;
|
||||
physical_size: number;
|
||||
physical_utilisation: number;
|
||||
}
|
||||
|
||||
export interface XenApiVm extends XenApiRecord {
|
||||
export interface XenApiVm extends XenApiRecord<"Vm"> {
|
||||
current_operations: Record<string, VM_OPERATION>;
|
||||
guest_metrics: string;
|
||||
metrics: string;
|
||||
metrics: XenApiVmMetrics["$ref"];
|
||||
name_label: string;
|
||||
name_description: string;
|
||||
power_state: POWER_STATE;
|
||||
resident_on: string;
|
||||
consoles: string[];
|
||||
resident_on: XenApiHost["$ref"];
|
||||
consoles: XenApiConsole["$ref"][];
|
||||
is_control_domain: boolean;
|
||||
is_a_snapshot: boolean;
|
||||
is_a_template: boolean;
|
||||
VCPUs_at_startup: number;
|
||||
}
|
||||
|
||||
export interface XenApiConsole extends XenApiRecord {
|
||||
export interface XenApiConsole extends XenApiRecord<"Console"> {
|
||||
protocol: string;
|
||||
location: string;
|
||||
}
|
||||
|
||||
export interface XenApiHostMetrics extends XenApiRecord {
|
||||
export interface XenApiHostMetrics extends XenApiRecord<"HostMetrics"> {
|
||||
live: boolean;
|
||||
memory_free: number;
|
||||
memory_total: number;
|
||||
}
|
||||
|
||||
export interface XenApiVmMetrics extends XenApiRecord {
|
||||
export interface XenApiVmMetrics extends XenApiRecord<"VmMetrics"> {
|
||||
VCPUs_number: number;
|
||||
}
|
||||
|
||||
export type XenApiVmGuestMetrics = XenApiRecord;
|
||||
export type XenApiVmGuestMetrics = XenApiRecord<"VmGuestMetrics">;
|
||||
|
||||
export interface XenApiTask extends XenApiRecord {
|
||||
export interface XenApiTask extends XenApiRecord<"Task"> {
|
||||
name_label: string;
|
||||
resident_on: string;
|
||||
resident_on: XenApiHost["$ref"];
|
||||
created: string;
|
||||
finished: string;
|
||||
status: string;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
export interface XenApiMessage extends XenApiRecord {
|
||||
export interface XenApiMessage extends XenApiRecord<"Message"> {
|
||||
name: string;
|
||||
cls: RawObjectType;
|
||||
}
|
||||
@@ -168,8 +170,8 @@ type WatchCallbackResult = {
|
||||
id: string;
|
||||
class: ObjectType;
|
||||
operation: "add" | "mod" | "del";
|
||||
ref: string;
|
||||
snapshot: RawXenApiRecord<XenApiRecord>;
|
||||
ref: XenApiRecord<string>["$ref"];
|
||||
snapshot: RawXenApiRecord<XenApiRecord<string>>;
|
||||
};
|
||||
|
||||
type WatchCallback = (results: WatchCallbackResult[]) => void;
|
||||
@@ -278,14 +280,16 @@ export default class XenApi {
|
||||
return fetch(url);
|
||||
}
|
||||
|
||||
async loadRecords<T extends XenApiRecord>(type: RawObjectType): Promise<T[]> {
|
||||
async loadRecords<T extends XenApiRecord<string>>(
|
||||
type: RawObjectType
|
||||
): Promise<T[]> {
|
||||
const result = await this.#call<{ [key: string]: RawXenApiRecord<T> }>(
|
||||
`${type}.get_all_records`,
|
||||
[this.sessionId]
|
||||
);
|
||||
|
||||
return Object.entries(result).map(([opaqueRef, record]) =>
|
||||
buildXoObject(record, { opaqueRef })
|
||||
buildXoObject(record, { opaqueRef: opaqueRef as T["$ref"] })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -324,7 +328,7 @@ export default class XenApi {
|
||||
this.#watchCallBack = callback;
|
||||
}
|
||||
|
||||
async injectWatchEvent(poolRef: string) {
|
||||
async injectWatchEvent(poolRef: XenApiPool["$ref"]) {
|
||||
this.#fromToken = await this.#call("event.inject", [
|
||||
this.sessionId,
|
||||
"pool",
|
||||
@@ -367,7 +371,7 @@ export default class XenApi {
|
||||
);
|
||||
},
|
||||
resume: (vmRefsWithPowerState: VmRefsWithPowerState) => {
|
||||
const vmRefs = Object.keys(vmRefsWithPowerState);
|
||||
const vmRefs = Object.keys(vmRefsWithPowerState) as XenApiVm["$ref"][];
|
||||
|
||||
return Promise.all(
|
||||
vmRefs.map((vmRef) => {
|
||||
@@ -394,7 +398,7 @@ export default class XenApi {
|
||||
);
|
||||
},
|
||||
clone: (vmRefsToClone: VmRefsToClone) => {
|
||||
const vmRefs = Object.keys(vmRefsToClone);
|
||||
const vmRefs = Object.keys(vmRefsToClone) as XenApiVm["$ref"][];
|
||||
|
||||
return Promise.all(
|
||||
vmRefs.map((vmRef) =>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { computed, type ComputedRef } from "vue";
|
||||
|
||||
type GetStatsExtension = {
|
||||
getStats: (
|
||||
hostUuid: string,
|
||||
hostUuid: XenApiHost["uuid"],
|
||||
granularity: GRANULARITY
|
||||
) => Promise<XapiStatsResponse<any>> | undefined;
|
||||
};
|
||||
@@ -31,7 +31,10 @@ export const useHostStore = defineStore("host", () => {
|
||||
const subscribe = createSubscribe<XenApiHost, Extensions>((options) => {
|
||||
const originalSubscription = hostCollection.subscribe(options);
|
||||
|
||||
const getStats = (hostUuid: string, granularity: GRANULARITY) => {
|
||||
const getStats = (
|
||||
hostUuid: XenApiHost["uuid"],
|
||||
granularity: GRANULARITY
|
||||
) => {
|
||||
const host = originalSubscription.getByUuid(hostUuid);
|
||||
|
||||
if (host === undefined) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { sortRecordsByNameLabel } from "@/libs/utils";
|
||||
import type { GRANULARITY, XapiStatsResponse } from "@/libs/xapi-stats";
|
||||
import { POWER_STATE } from "@/libs/xen-api";
|
||||
import type { XenApiHost, XenApiVm } from "@/libs/xen-api";
|
||||
import { POWER_STATE } from "@/libs/xen-api";
|
||||
import { useXapiCollectionStore } from "@/stores/xapi-collection.store";
|
||||
import { useXenApiStore } from "@/stores/xen-api.store";
|
||||
import { createSubscribe, type Subscription } from "@/types/xapi-collection";
|
||||
@@ -9,14 +9,14 @@ import { defineStore } from "pinia";
|
||||
import { computed, type ComputedRef } from "vue";
|
||||
|
||||
type DefaultExtension = {
|
||||
recordsByHostRef: ComputedRef<Map<string, XenApiVm[]>>;
|
||||
recordsByHostRef: ComputedRef<Map<XenApiHost["$ref"], XenApiVm[]>>;
|
||||
runningVms: ComputedRef<XenApiVm[]>;
|
||||
};
|
||||
|
||||
type GetStatsExtension = [
|
||||
{
|
||||
getStats: (
|
||||
id: string,
|
||||
id: XenApiVm["uuid"],
|
||||
granularity: GRANULARITY
|
||||
) => Promise<XapiStatsResponse<any>>;
|
||||
},
|
||||
@@ -39,7 +39,7 @@ export const useVmStore = defineStore("vm", () => {
|
||||
|
||||
const extendedSubscription = {
|
||||
recordsByHostRef: computed(() => {
|
||||
const vmsByHostOpaqueRef = new Map<string, XenApiVm[]>();
|
||||
const vmsByHostOpaqueRef = new Map<XenApiHost["$ref"], XenApiVm[]>();
|
||||
|
||||
originalSubscription.records.value.forEach((vm) => {
|
||||
if (!vmsByHostOpaqueRef.has(vm.resident_on)) {
|
||||
@@ -61,23 +61,23 @@ export const useVmStore = defineStore("vm", () => {
|
||||
const hostSubscription = options?.hostSubscription;
|
||||
|
||||
const getStatsSubscription = hostSubscription !== undefined && {
|
||||
getStats: (id: string, granularity: GRANULARITY) => {
|
||||
getStats: (vmUuid: XenApiVm["uuid"], granularity: GRANULARITY) => {
|
||||
const xenApiStore = useXenApiStore();
|
||||
|
||||
if (!xenApiStore.isConnected) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const vm = originalSubscription.getByUuid(id);
|
||||
const vm = originalSubscription.getByUuid(vmUuid);
|
||||
|
||||
if (vm === undefined) {
|
||||
throw new Error(`VM ${id} could not be found.`);
|
||||
throw new Error(`VM ${vmUuid} could not be found.`);
|
||||
}
|
||||
|
||||
const host = hostSubscription.getByOpaqueRef(vm.resident_on);
|
||||
|
||||
if (host === undefined) {
|
||||
throw new Error(`VM ${id} is halted or host could not be found.`);
|
||||
throw new Error(`VM ${vmUuid} is halted or host could not be found.`);
|
||||
}
|
||||
|
||||
return xenApiStore.getXapiStats()._getAndUpdateStats({
|
||||
|
||||
@@ -16,7 +16,7 @@ export const useXapiCollectionStore = defineStore("xapiCollection", () => {
|
||||
|
||||
function get<
|
||||
T extends RawObjectType,
|
||||
S extends XenApiRecord = RawTypeToObject[T]
|
||||
S extends XenApiRecord<string> = RawTypeToObject[T]
|
||||
>(type: T): ReturnType<typeof createXapiCollection<S>> {
|
||||
if (!collections.value.has(type)) {
|
||||
collections.value.set(type, createXapiCollection<S>(type));
|
||||
@@ -28,15 +28,17 @@ export const useXapiCollectionStore = defineStore("xapiCollection", () => {
|
||||
return { get };
|
||||
});
|
||||
|
||||
const createXapiCollection = <T extends XenApiRecord>(type: RawObjectType) => {
|
||||
const createXapiCollection = <T extends XenApiRecord<string>>(
|
||||
type: RawObjectType
|
||||
) => {
|
||||
const isReady = ref(false);
|
||||
const isFetching = ref(false);
|
||||
const isReloading = computed(() => isReady.value && isFetching.value);
|
||||
const lastError = ref<string>();
|
||||
const hasError = computed(() => lastError.value !== undefined);
|
||||
const subscriptions = ref(new Set<symbol>());
|
||||
const recordsByOpaqueRef = ref(new Map<string, T>());
|
||||
const recordsByUuid = ref(new Map<string, T>());
|
||||
const recordsByOpaqueRef = ref(new Map<T["$ref"], T>());
|
||||
const recordsByUuid = ref(new Map<T["uuid"], T>());
|
||||
const filter = ref<(record: T) => boolean>();
|
||||
const sort = ref<(record1: T, record2: T) => 1 | 0 | -1>();
|
||||
const xenApiStore = useXenApiStore();
|
||||
@@ -54,12 +56,12 @@ const createXapiCollection = <T extends XenApiRecord>(type: RawObjectType) => {
|
||||
return filter.value !== undefined ? records.filter(filter.value) : records;
|
||||
});
|
||||
|
||||
const getByOpaqueRef = (opaqueRef: string) =>
|
||||
const getByOpaqueRef = (opaqueRef: T["$ref"]) =>
|
||||
recordsByOpaqueRef.value.get(opaqueRef);
|
||||
|
||||
const getByUuid = (uuid: string) => recordsByUuid.value.get(uuid);
|
||||
const getByUuid = (uuid: T["uuid"]) => recordsByUuid.value.get(uuid);
|
||||
|
||||
const hasUuid = (uuid: string) => recordsByUuid.value.has(uuid);
|
||||
const hasUuid = (uuid: T["uuid"]) => recordsByUuid.value.has(uuid);
|
||||
|
||||
const hasSubscriptions = computed(() => subscriptions.value.size > 0);
|
||||
|
||||
@@ -89,7 +91,7 @@ const createXapiCollection = <T extends XenApiRecord>(type: RawObjectType) => {
|
||||
recordsByUuid.value.set(record.uuid, record);
|
||||
};
|
||||
|
||||
const remove = (opaqueRef: string) => {
|
||||
const remove = (opaqueRef: T["$ref"]) => {
|
||||
if (!recordsByOpaqueRef.value.has(opaqueRef)) {
|
||||
return;
|
||||
}
|
||||
@@ -129,7 +131,7 @@ const createXapiCollection = <T extends XenApiRecord>(type: RawObjectType) => {
|
||||
|
||||
if (options?.immediate !== false) {
|
||||
start();
|
||||
return subscription as Subscription<T, O>;
|
||||
return subscription as unknown as Subscription<T, O>;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -39,17 +39,16 @@ export const useXenApiStore = defineStore("xen-api", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const buildObject = () =>
|
||||
buildXoObject(result.snapshot, { opaqueRef: result.ref }) as any;
|
||||
|
||||
switch (result.operation) {
|
||||
case "add":
|
||||
return collection.add(
|
||||
buildXoObject(result.snapshot, { opaqueRef: result.ref })
|
||||
);
|
||||
return collection.add(buildObject());
|
||||
case "mod":
|
||||
return collection.update(
|
||||
buildXoObject(result.snapshot, { opaqueRef: result.ref })
|
||||
);
|
||||
return collection.update(buildObject());
|
||||
case "del":
|
||||
return collection.remove(result.ref);
|
||||
return collection.remove(result.ref as any);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,11 +13,11 @@ import type {
|
||||
} from "@/libs/xen-api";
|
||||
import type { ComputedRef, Ref } from "vue";
|
||||
|
||||
type DefaultExtension<T extends XenApiRecord> = {
|
||||
type DefaultExtension<T extends XenApiRecord<string>> = {
|
||||
records: ComputedRef<T[]>;
|
||||
getByOpaqueRef: (opaqueRef: string) => T | undefined;
|
||||
getByUuid: (uuid: string) => T | undefined;
|
||||
hasUuid: (uuid: string) => boolean;
|
||||
getByOpaqueRef: (opaqueRef: T["$ref"]) => T | undefined;
|
||||
getByUuid: (uuid: T["uuid"]) => T | undefined;
|
||||
hasUuid: (uuid: T["uuid"]) => boolean;
|
||||
isReady: Readonly<Ref<boolean>>;
|
||||
isFetching: Readonly<Ref<boolean>>;
|
||||
isReloading: ComputedRef<boolean>;
|
||||
@@ -33,7 +33,7 @@ type DeferExtension = [
|
||||
{ immediate: false }
|
||||
];
|
||||
|
||||
type DefaultExtensions<T extends XenApiRecord> = [
|
||||
type DefaultExtensions<T extends XenApiRecord<string>> = [
|
||||
DefaultExtension<T>,
|
||||
DeferExtension
|
||||
];
|
||||
@@ -64,14 +64,14 @@ type GenerateSubscription<
|
||||
: object;
|
||||
|
||||
export type Subscription<
|
||||
T extends XenApiRecord,
|
||||
T extends XenApiRecord<string>,
|
||||
Options extends object,
|
||||
Extensions extends any[] = []
|
||||
> = GenerateSubscription<Options, Extensions> &
|
||||
GenerateSubscription<Options, DefaultExtensions<T>>;
|
||||
|
||||
export function createSubscribe<
|
||||
T extends XenApiRecord,
|
||||
T extends XenApiRecord<string>,
|
||||
Extensions extends any[],
|
||||
Options extends object = SubscribeOptions<Extensions>
|
||||
>(builder: (options?: Options) => Subscription<T, Options, Extensions>) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ObjectNotFoundWrapper from "@/components/ObjectNotFoundWrapper.vue";
|
||||
import type { XenApiHost } from "@/libs/xen-api";
|
||||
import { useHostStore } from "@/stores/host.store";
|
||||
import { useUiStore } from "@/stores/ui.store";
|
||||
import { watchEffect } from "vue";
|
||||
@@ -16,6 +17,8 @@ const route = useRoute();
|
||||
const uiStore = useUiStore();
|
||||
|
||||
watchEffect(() => {
|
||||
uiStore.currentHostOpaqueRef = getByUuid(route.params.uuid as string)?.$ref;
|
||||
uiStore.currentHostOpaqueRef = getByUuid(
|
||||
route.params.uuid as XenApiHost["uuid"]
|
||||
)?.$ref;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { POWER_STATE, VM_OPERATION } from "@/libs/xen-api";
|
||||
import { POWER_STATE, VM_OPERATION, type XenApiVm } from "@/libs/xen-api";
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import RemoteConsole from "@/components/RemoteConsole.vue";
|
||||
@@ -36,7 +36,7 @@ const { isReady: isConsoleReady, getByOpaqueRef: getConsoleByOpaqueRef } =
|
||||
|
||||
const isReady = computed(() => isVmReady.value && isConsoleReady.value);
|
||||
|
||||
const vm = computed(() => getVmByUuid(route.params.uuid as string));
|
||||
const vm = computed(() => getVmByUuid(route.params.uuid as XenApiVm["uuid"]));
|
||||
|
||||
const isVmRunning = computed(
|
||||
() => vm.value?.power_state === POWER_STATE.RUNNING
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import ObjectNotFoundWrapper from "@/components/ObjectNotFoundWrapper.vue";
|
||||
import VmHeader from "@/components/vm/VmHeader.vue";
|
||||
import VmTabBar from "@/components/vm/VmTabBar.vue";
|
||||
import type { XenApiVm } from "@/libs/xen-api";
|
||||
import { useUiStore } from "@/stores/ui.store";
|
||||
import { useVmStore } from "@/stores/vm.store";
|
||||
import { whenever } from "@vueuse/core";
|
||||
@@ -19,6 +20,6 @@ import { useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
const { getByUuid, hasUuid, isReady } = useVmStore().subscribe();
|
||||
const uiStore = useUiStore();
|
||||
const vm = computed(() => getByUuid(route.params.uuid as string));
|
||||
const vm = computed(() => getByUuid(route.params.uuid as XenApiVm["uuid"]));
|
||||
whenever(vm, (vm) => (uiStore.currentHostOpaqueRef = vm.resident_on));
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user