Skip to content

Commit

Permalink
refactor(renterd): config only patch changed
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Nov 18, 2024
1 parent e21682a commit 571da0c
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 57 deletions.
76 changes: 76 additions & 0 deletions apps/renterd/contexts/config/payloadPatches.ts
Original file line number Diff line number Diff line change
@@ -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> = T extends Function
? T
: T extends Array<infer U>
? Array<DeepPartial<U>>
: T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: 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<T>(existing: T, staged: T): DeepPartial<T> | undefined {
if (isEqual(existing, staged)) {
return undefined
}

if (!isObject(existing) || !isObject(staged)) {
return staged as DeepPartial<T>
}

const keysList = union(keys(existing), keys(staged)) as Array<keyof T>

const result = {} as DeepPartial<T>
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
}
15 changes: 5 additions & 10 deletions apps/renterd/contexts/config/transformUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
104 changes: 59 additions & 45 deletions apps/renterd/contexts/config/useOnValid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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,
Expand All @@ -29,13 +29,11 @@ export function useOnValid({
resources: ResourcesMaybeLoaded
revalidateAndResetForm: () => Promise<void>
}) {
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(
Expand All @@ -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: {
Expand Down Expand Up @@ -115,11 +130,10 @@ export function useOnValid({
[
resources,
renterdState.data,
isAutopilotEnabled,
autopilotUpdate,
settingsGougingUpdate,
settingsPinnedUpdate,
settingsUploadUpdate,
autopilotPatch,
settingsGougingPatch,
settingsPinnedPatch,
settingsUploadPatch,
revalidateAndResetForm,
autopilotTrigger,
mutate,
Expand Down
4 changes: 2 additions & 2 deletions apps/renterd/contexts/config/useResources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down

0 comments on commit 571da0c

Please sign in to comment.