From b4300a28cca2469db1080935e79f9f6556a66808 Mon Sep 17 00:00:00 2001 From: Alex Freska Date: Mon, 18 Nov 2024 15:25:08 -0500 Subject: [PATCH] refactor(renterd): config only patch changed --- .../renterd/contexts/config/payloadPatches.ts | 76 +++++++++++++ apps/renterd/contexts/config/transformUp.ts | 15 +-- apps/renterd/contexts/config/useOnValid.tsx | 104 ++++++++++-------- apps/renterd/contexts/config/useResources.tsx | 4 +- 4 files changed, 142 insertions(+), 57 deletions(-) create mode 100644 apps/renterd/contexts/config/payloadPatches.ts diff --git a/apps/renterd/contexts/config/payloadPatches.ts b/apps/renterd/contexts/config/payloadPatches.ts new file mode 100644 index 000000000..28622a91f --- /dev/null +++ b/apps/renterd/contexts/config/payloadPatches.ts @@ -0,0 +1,76 @@ +import { + AutopilotConfig, + SettingsGouging, + SettingsPinned, + SettingsUpload, +} from '@siafoundation/renterd-types' +import { isObject, isEqual, union, keys } from '@technically/lodash' +import { ResourcesRequiredLoaded } from './useResources' + +/** + * @param param0 The resources and staged objects. + * @returns An object with each patch payload or undefined if no changes. + */ +export function getPatches({ + resources, + staged, +}: { + resources: ResourcesRequiredLoaded + staged: { + autopilot: AutopilotConfig + gouging: SettingsGouging + pinned: SettingsPinned + upload: SettingsUpload + } +}) { + return { + autopilot: getPatch(resources.autopilot.data, staged.autopilot), + gouging: getPatch(resources.gouging.data, staged.gouging), + pinned: getPatch(resources.pinned.data, staged.pinned), + upload: getPatch(resources.upload.data, staged.upload), + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +type DeepPartial = T extends Function + ? T + : T extends Array + ? Array> + : T extends object + ? { [K in keyof T]?: DeepPartial } + : T + +/** + * @param existing The existing object. + * @param staged The staged object. + * @returns A partial object of T with the differences between existing and staged. + */ +function getPatch(existing: T, staged: T): DeepPartial | undefined { + if (isEqual(existing, staged)) { + return undefined + } + + if (!isObject(existing) || !isObject(staged)) { + return staged as DeepPartial + } + + const keysList = union(keys(existing), keys(staged)) as Array + + const result = {} as DeepPartial + let isChanged = false + + keysList.forEach((key) => { + const origValue = existing[key] + const stagedValue = staged[key] + + const valueChange = getPatch(origValue, stagedValue) + + if (valueChange !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-extra-semi, @typescript-eslint/no-explicit-any + ;(result as any)[key] = valueChange + isChanged = true + } + }) + + return isChanged ? result : undefined +} diff --git a/apps/renterd/contexts/config/transformUp.ts b/apps/renterd/contexts/config/transformUp.ts index dbbbdce68..641710df7 100644 --- a/apps/renterd/contexts/config/transformUp.ts +++ b/apps/renterd/contexts/config/transformUp.ts @@ -171,22 +171,17 @@ export function transformUpUpload( export function transformUp({ resources, renterdState, - isAutopilotEnabled, values, }: { resources: ResourcesRequiredLoaded renterdState: { network: 'mainnet' | 'zen' | 'anagami' } - isAutopilotEnabled: boolean values: SubmitValues }) { - const autopilot = isAutopilotEnabled - ? transformUpAutopilot( - renterdState.network, - values, - resources.autopilot.data - ) - : undefined - + const autopilot = transformUpAutopilot( + renterdState.network, + values, + resources.autopilot.data + ) const gouging = transformUpGouging(values, resources.gouging.data) const pinned = transformUpPinned( values, diff --git a/apps/renterd/contexts/config/useOnValid.tsx b/apps/renterd/contexts/config/useOnValid.tsx index 742146d04..6787588d9 100644 --- a/apps/renterd/contexts/config/useOnValid.tsx +++ b/apps/renterd/contexts/config/useOnValid.tsx @@ -4,12 +4,12 @@ import { } from '@siafoundation/design-system' import { delay, useMutate } from '@siafoundation/react-core' import { - useAutopilotConfigUpdate, useAutopilotTrigger, useBusState, - useSettingsGougingUpdate, - useSettingsPinnedUpdate, - useSettingsUploadUpdate, + useAutopilotConfigPatch, + useSettingsGougingPatch, + useSettingsPinnedPatch, + useSettingsUploadPatch, } from '@siafoundation/renterd-react' import { busHostsRoute } from '@siafoundation/renterd-types' import { useCallback } from 'react' @@ -20,7 +20,7 @@ import { } from './useResources' import { transformUp } from './transformUp' import { InputValues, SubmitValues } from './types' -import { useApp } from '../app' +import { getPatches } from './payloadPatches' export function useOnValid({ resources, @@ -29,13 +29,11 @@ export function useOnValid({ resources: ResourcesMaybeLoaded revalidateAndResetForm: () => Promise }) { - const app = useApp() - const isAutopilotEnabled = !!app.autopilotInfo.data?.isAutopilotEnabled const autopilotTrigger = useAutopilotTrigger() - const autopilotUpdate = useAutopilotConfigUpdate() - const settingsGougingUpdate = useSettingsGougingUpdate() - const settingsPinnedUpdate = useSettingsPinnedUpdate() - const settingsUploadUpdate = useSettingsUploadUpdate() + const autopilotPatch = useAutopilotConfigPatch() + const settingsGougingPatch = useSettingsGougingPatch() + const settingsPinnedPatch = useSettingsPinnedPatch() + const settingsUploadPatch = useSettingsUploadPatch() const renterdState = useBusState() const mutate = useMutate() const onValid = useCallback( @@ -45,49 +43,66 @@ export function useOnValid({ if (!loaded || !renterdState.data) { return } - const firstTimeSettingConfig = - isAutopilotEnabled && !resources.autopilot.data + // TODO: is this still correct? + const firstTimeSettingConfig = !resources.autopilot.data const { payloads } = transformUp({ resources: resources as ResourcesRequiredLoaded, renterdState: renterdState.data, - isAutopilotEnabled, values: values as SubmitValues, }) - const autopilotResponse = payloads.autopilot - ? await autopilotUpdate.put({ - payload: payloads.autopilot, + const patches = getPatches({ + resources: resources as ResourcesRequiredLoaded, + staged: payloads, + }) + + const promises = [] + + if (patches.autopilot) { + promises.push( + autopilotPatch.patch({ + payload: patches.autopilot, + }) + ) + } + + if (patches.gouging) { + promises.push( + settingsGougingPatch.patch({ + payload: patches.gouging, + }) + ) + } + + if (patches.pinned) { + promises.push( + settingsPinnedPatch.patch({ + payload: patches.pinned, + }) + ) + } + + if (patches.upload) { + promises.push( + settingsUploadPatch.patch({ + payload: patches.upload, }) - : undefined - - const [gougingResponse, pinnedResponse, uploadResponse] = - await Promise.all([ - settingsGougingUpdate.put({ - payload: payloads.gouging, - }), - settingsPinnedUpdate.put({ - payload: payloads.pinned, - }), - settingsUploadUpdate.put({ - payload: payloads.upload, - }), - ]) - - const error = - autopilotResponse?.error || - gougingResponse.error || - pinnedResponse.error || - uploadResponse.error - if (error) { + ) + } + + const results = await Promise.all(promises) + + const err = results.find((result) => result.error) + if (err) { triggerErrorToast({ title: 'Error updating configuration', - body: error, + body: err.error, }) return } - if (isAutopilotEnabled && payloads.autopilot) { + if (patches.autopilot) { // Trigger the autopilot loop with new settings applied. autopilotTrigger.post({ payload: { @@ -115,11 +130,10 @@ export function useOnValid({ [ resources, renterdState.data, - isAutopilotEnabled, - autopilotUpdate, - settingsGougingUpdate, - settingsPinnedUpdate, - settingsUploadUpdate, + autopilotPatch, + settingsGougingPatch, + settingsPinnedPatch, + settingsUploadPatch, revalidateAndResetForm, autopilotTrigger, mutate, diff --git a/apps/renterd/contexts/config/useResources.tsx b/apps/renterd/contexts/config/useResources.tsx index f06029b0e..6be10dd2c 100644 --- a/apps/renterd/contexts/config/useResources.tsx +++ b/apps/renterd/contexts/config/useResources.tsx @@ -193,11 +193,11 @@ export type ResourcesMaybeLoaded = { export type ResourcesRequiredLoaded = { autopilotInfo: { - data?: AutopilotInfo + data: AutopilotInfo error?: SWRError } autopilot: { - data?: AutopilotConfig + data: AutopilotConfig error?: SWRError } gouging: {