Skip to content

Commit

Permalink
wip: allow selecting space policy
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Dec 3, 2024
1 parent 3e4c684 commit 49e5f85
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 34 deletions.
3 changes: 3 additions & 0 deletions web/src/api/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const fetchConfigModel = (): Promise<configModel.Config | undefined> =>

const setConfig = (config: config.Config) => put("/api/storage/config", config);

const setConfigModel = (model: configModel.Config) => put("/api/storage/config_model", model);

/**
* Returns the list of jobs
*/
Expand Down Expand Up @@ -69,6 +71,7 @@ export {
fetchConfig,
fetchConfigModel,
setConfig,
setConfigModel,
fetchStorageJobs,
findStorageJob,
refresh,
Expand Down
42 changes: 30 additions & 12 deletions web/src/components/storage/DriveEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { _, formatList } from "~/i18n";
import { sprintf } from "sprintf-js";
import { baseName, deviceLabel, formattedPath, SPACE_POLICIES } from "~/components/storage/utils";
import { useAvailableDevices } from "~/queries/storage";
import { config as type } from "~/api/storage/types";
import { configModel } from "~/api/storage/types";
import { StorageDevice } from "~/types/storage";
import * as driveUtils from "~/components/storage/utils/drive";
import { typeDescription, contentDescription } from "~/components/storage/utils/device";
Expand All @@ -50,7 +50,9 @@ import {
MenuToggle,
} from "@patternfly/react-core";

type DriveEditorProps = { drive: type.DriveElement; driveDevice: StorageDevice };
import { useSpacePolicyMutation, useDriveNameMutation } from "~/queries/storage";

type DriveEditorProps = { drive: configModel.Drive; driveDevice: StorageDevice };

// FIXME: Presentation is quite poor
const SpacePolicySelectorIntro = ({ device }) => {
Expand Down Expand Up @@ -82,11 +84,16 @@ const SpacePolicySelectorIntro = ({ device }) => {
);
};

const SpacePolicySelector = ({ drive, driveDevice }) => {
const SpacePolicySelector = ({ drive, driveDevice }: DriveEditorProps) => {
const menuRef = useRef();
const toggleMenuRef = useRef();
const [isOpen, setIsOpen] = useState(false);
const changePolicy = useSpacePolicyMutation();
const onToggle = () => setIsOpen(!isOpen);
const onPolicyChange = (policy: configModel.SpacePolicy) => {
changePolicy(drive.name, policy);
setIsOpen(false);
};

const currentPolicy = driveUtils.spacePolicyEntry(drive);

Expand All @@ -96,7 +103,12 @@ const SpacePolicySelector = ({ drive, driveDevice }) => {
const Name = () => (isSelected ? <b>{policy.label}</b> : policy.label);

return (
<MenuItem itemId={policy.id} isSelected={isSelected} description={policy.description}>
<MenuItem
itemId={policy.id}
isSelected={isSelected}
description={policy.description}
onClick={() => onPolicyChange(policy.id)}
>
<Name />
</MenuItem>
);
Expand Down Expand Up @@ -268,7 +280,7 @@ const SearchSelectorIntro = ({ drive }) => {
);
};

const SearchSelectorMultipleOptions = ({ selected, withNewVg }) => {
const SearchSelectorMultipleOptions = ({ selected, withNewVg, onChange }) => {
const devices = useAvailableDevices();

// FIXME: Presentation is quite poor
Expand Down Expand Up @@ -319,6 +331,7 @@ const SearchSelectorMultipleOptions = ({ selected, withNewVg }) => {
itemId={device.sid}
isSelected={isSelected}
description={<DeviceDescription device={device} />}
onClick={() => onChange(device.name)}
>
<Name />
</MenuItem>
Expand All @@ -342,25 +355,25 @@ const SearchSelectorSingleOption = ({ selected }) => {
);
};

const SearchSelectorOptions = ({ drive, selected }) => {
const SearchSelectorOptions = ({ drive, selected, onChange }) => {
if (driveUtils.hasReuse(drive)) return <SearchSelectorSingleOption selected={selected} />;

if (!driveUtils.hasFilesystem(drive)) {
if (driveUtils.hasPv(drive) || driveUtils.explicitBoot(drive)) {
return <SearchSelectorSingleOption selected={selected} />;
}

return <SearchSelectorMultipleOptions selected={selected} />;
return <SearchSelectorMultipleOptions selected={selected} onChange={onChange} />;
}

return <SearchSelectorMultipleOptions selected={selected} withNewVg />;
return <SearchSelectorMultipleOptions selected={selected} withNewVg onChange={onChange} />;
};

const SearchSelector = ({ drive, selected }) => {
const SearchSelector = ({ drive, selected, onChange }) => {
return (
<>
<SearchSelectorIntro drive={drive} />
<SearchSelectorOptions drive={drive} selected={selected} />
<SearchSelectorOptions drive={drive} selected={selected} onChange={onChange} />
</>
);
};
Expand All @@ -384,6 +397,11 @@ const DriveSelector = ({ drive, selected }) => {
const menuRef = useRef();
const toggleMenuRef = useRef();
const [isOpen, setIsOpen] = useState(false);
const changeName = useDriveNameMutation();
const onNameChange = (newName: string) => {
changeName(drive.name, newName);
setIsOpen(false);
};
const onToggle = () => setIsOpen(!isOpen);

return (
Expand All @@ -409,7 +427,7 @@ const DriveSelector = ({ drive, selected }) => {
<Menu ref={menuRef} activeItemId={selected.sid}>
<MenuContent>
<MenuList>
<SearchSelector drive={drive} selected={selected} />
<SearchSelector drive={drive} selected={selected} onChange={onNameChange} />
<RemoveDriveOption drive={drive} />
</MenuList>
</MenuContent>
Expand All @@ -421,7 +439,7 @@ const DriveSelector = ({ drive, selected }) => {
};

const DriveHeader = ({ drive, driveDevice }: DriveEditorProps) => {
const text = (drive: type.DriveElement): string => {
const text = (drive: configModel.Drive): string => {
if (driveUtils.hasRoot(drive)) {
if (driveUtils.hasPv(drive)) {
if (drive.boot) {
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/storage/utils/drive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@
// @ts-check

import { _, n_, formatList } from "~/i18n";
import { DriveElement } from "~/api/storage/types";
import { configModel } from "~/api/storage/types";
import { SpacePolicy, SPACE_POLICIES, baseName, formattedPath } from "~/components/storage/utils";
import * as partitionUtils from "~/components/storage/utils/partition";

/**
* String to identify the drive.
*/
const label = (drive: DriveElement): string => {
const label = (drive: configModel.Drive): string => {
if (drive.alias) return drive.alias;

return baseName(drive.name);
};

const spacePolicyEntry = (drive: DriveElement): SpacePolicy => {
const spacePolicyEntry = (drive: configModel.Drive): SpacePolicy => {
return SPACE_POLICIES.find((p) => p.id === drive.spacePolicy);
};

Expand Down
22 changes: 3 additions & 19 deletions web/src/queries/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
useSuspenseQuery,
} from "@tanstack/react-query";
import React from "react";
import { fetchConfig, fetchConfigModel, setConfig } from "~/api/storage";
import { fetchConfig, setConfig } from "~/api/storage";
import { fetchDevices, fetchDevicesDirty } from "~/api/storage/devices";
import {
calculate,
Expand All @@ -40,7 +40,6 @@ import {
import { useInstallerClient } from "~/context/installer";
import {
config,
configModel,
ProductParams,
Volume as APIVolume,
ProposalSettingsPatch,
Expand All @@ -61,12 +60,6 @@ const configQuery = {
staleTime: Infinity,
};

const configModelQuery = {
queryKey: ["storage", "configModel"],
queryFn: fetchConfigModel,
staleTime: Infinity,
};

const devicesQuery = (scope: "result" | "system") => ({
queryKey: ["storage", "devices", scope],
queryFn: () => fetchDevices(scope),
Expand Down Expand Up @@ -127,16 +120,6 @@ const useConfig = (options?: QueryHookOptions): config.Config => {
return data;
};

/**
* Hook that returns the config model.
*/
const useConfigModel = (options?: QueryHookOptions): configModel.Config => {
const query = configModelQuery;
const func = options?.suspense ? useSuspenseQuery : useQuery;
const { data } = func(query);
return data;
};

/**
* Hook for setting a new config.
*/
Expand Down Expand Up @@ -334,7 +317,6 @@ const useDeprecatedChanges = () => {
export {
useConfig,
useConfigMutation,
useConfigModel,
useDevices,
useAvailableDevices,
useProductParams,
Expand All @@ -345,3 +327,5 @@ export {
useDeprecated,
useDeprecatedChanges,
};

export * from "~/queries/storage/config-model";
112 changes: 112 additions & 0 deletions web/src/queries/storage/config-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) [2024] SUSE LLC
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact SUSE LLC.
*
* To contact SUSE LLC about this file by physical or electronic mail, you may
* find current contact information at www.suse.com.
*/

import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from "@tanstack/react-query";
import { fetchConfigModel, setConfigModel } from "~/api/storage";
import { configModel } from "~/api/storage/types";
import { QueryHookOptions } from "~/types/queries";

function findDrive(model: configModel.Config, name: string): configModel.Drive | undefined {
const drives = model.drives || [];
return drives.find((d) => d.name === name);
}

function setDriveName(
model: configModel.Config,
name: string,
newName: string,
): configModel.Config {
const drive = findDrive(model, name);
if (drive !== undefined) drive.name = newName;

return model;
}

function setSpacePolicy(
model: configModel.Config,
name: string,
spacePolicy: configModel.SpacePolicy,
): configModel.Config {
const drive = findDrive(model, name);
if (drive !== undefined) drive.spacePolicy = spacePolicy;

return model;
}

const configModelQuery = {
queryKey: ["storage", "configModel"],
queryFn: fetchConfigModel,
staleTime: Infinity,
};

/**
* Hook that returns the config model.
*/
const useConfigModel = (options?: QueryHookOptions): configModel.Config => {
const query = configModelQuery;
const func = options?.suspense ? useSuspenseQuery : useQuery;
const { data } = func(query);
return data;
};

/**
* Hook for setting a new config model.
*/
const useConfigModelMutation = () => {
const queryClient = useQueryClient();
const query = {
mutationFn: (model: configModel.Config) => setConfigModel(model),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["storage"] }),
};

return useMutation(query);
};

const useConfigModelActionMutation = () => {
const originalModel = useConfigModel({ suspense: true });
const { mutate } = useConfigModelMutation();

const model = JSON.parse(JSON.stringify(originalModel));

return (action: (model: configModel.Config) => void) => {
action(model);
mutate(model);
};
};

const useDriveNameMutation = () => {
const action = useConfigModelActionMutation();

return (name: string, newName: string) => {
action((model: configModel.Config) => setDriveName(model, name, newName));
};
};

const useSpacePolicyMutation = () => {
const actionMutation = useConfigModelActionMutation();

return (name: string, spacePolicy: configModel.SpacePolicy) => {
actionMutation((model: configModel.Config) => setSpacePolicy(model, name, spacePolicy));
};
};

export { useConfigModel, useConfigModelMutation, useDriveNameMutation, useSpacePolicyMutation };

0 comments on commit 49e5f85

Please sign in to comment.