-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Read settings from props/server config when available #1558
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #1558 +/- ##
==========================================
- Coverage 46.66% 46.63% -0.03%
==========================================
Files 591 591
Lines 36394 36406 +12
Branches 9108 9113 +5
==========================================
- Hits 16984 16979 -5
- Misses 19358 19375 +17
Partials 52 52
Flags with carried forward coverage won't be shown. Click here to find out more.
☔ View full report in Codecov by Sentry. |
LocalWorkspaceStorage.getServerConfigValueOrUseDefault( | ||
serverConfigValues, | ||
'dateTimeFormat', | ||
DateTimeColumnFormatter.DEFAULT_DATETIME_FORMAT_STRING | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of setting the value, we should leave it undefined
; that way if the server value is updated again, user gets the new value unless they've explicitly updated the option before.
That's how we handle it on Enterprise, anyway: https://github.com/deephaven-ent/iris/blob/b41e56506498118f27fed79ccac41f3dc2af4617/web/client-ui/src/dashboard/WorkspaceStorage.ts#L174
https://github.com/deephaven-ent/iris/blob/b41e56506498118f27fed79ccac41f3dc2af4617/web/client-ui/src/settings/FormattingSectionContent.tsx#L857
From a previous commit I did: https://github.com/deephaven-ent/iris/commit/28a75ae75b27bf97fa1046418af89aa16986e412
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you include the steps you took to configure the server/test different default values being read?
packages/redux/src/actions.ts
Outdated
export const saveSettings = | ||
( | ||
settings: WorkspaceSettings | ||
settings: Partial<WorkspaceSettings> | ||
): ThunkAction< | ||
Promise<Workspace>, | ||
Promise<CustomizableWorkspace>, | ||
RootState, | ||
never, | ||
PayloadAction<unknown> | ||
> => | ||
dispatch => | ||
dispatch(updateWorkspaceData({ settings })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather you make this a new action updateSettings
rather than saveSettings
. Otherwise there's no way to delete an old setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
old settings can be deleted by setting it's value to undefined. Although I do agree updateSettings make more sense semantically
packages/redux/src/selectors.ts
Outdated
// this will only be null on initial render | ||
return null as never; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should just throw
in this case ...
// this will only be null on initial render | |
return null as never; | |
throw new Error('getWorkspace called before workspace was set') |
Then in AppInit
we should catch that error in the mapStateToProps
and set workspace to null
there. That will achieve two things:
- A more descriptive error about how to fix the issue rather than just getting a possibly inexplicable NPE
- More accurate depiction in
AppInit
of what's going on with theworkspace
prop. Right now it's defined as justWorkspace
but we're still expecting/checking if it'snull
to know ifisLoading
is still ongoing, which is kind of ignoring the type checking.
packages/redux/src/selectors.ts
Outdated
const defaultInjectedWorkspace: Workspace = { | ||
data: { | ||
...workspace.data, | ||
settings: getDefaultWorkspaceSettings(store) ?? {}, | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right idea, but I think we're injecting at the wrong spot. We should inject just the settings in getSettings
instead of injecting whenever the workspace is fetched.
Also, we should memoize the result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we are fetching the entire workspace then manually destructure the settings object from it somewhere. My original intention was so that the Workspace object would look identical to before the change, to avoid creating unintended changes elsewhere
packages/redux/src/selectors.ts
Outdated
const keys = Object.keys( | ||
customizedWorkspaceSettings | ||
) as (keyof WorkspaceSettings)[]; | ||
|
||
for (let i = 0; i < keys.length; i += 1) { | ||
const key = keys[i]; | ||
const value = customizedWorkspaceSettings[key]; | ||
// only set pass in customized defaults if defined | ||
if (value !== undefined) { | ||
defaultInjectedWorkspace.data.settings[key] = | ||
value as WorkspaceSettings[typeof key] as never; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsure why you wrote it this way - pretty sure when injecting in getSettings
, you could just do something like:
export const getSettings = <State extends RootState>(
store: State
): WorkspaceSettings => {
const defaultSettings = getDefaultWorkspaceSettings(store) ?? EMPTY_OBJECT;
const userSettings = getWorkspace(store).data.settings;
return {
...defaultSettings,
...userSettings,
};
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was because sometimes the userSettings have values intentionally set to undefined (not just missing) to let user reset values to default etc. setting a value to undefined means the value is still keyed in userSettings so doing object spreading would make those values undefined as opposed to the default.
defaultDateTimeFormat?: string; | ||
showTimeZone?: boolean; | ||
showTSeparator?: boolean; | ||
timeZone?: string; | ||
truncateNumbersWithPound?: boolean; | ||
saveSettings: (settings: Partial<WorkspaceSettings>) => void; | ||
defaultDecimalFormatOptions?: FormatOption; | ||
defaultIntegerFormatOptions?: FormatOption; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these should be optional, the value we get from redux should always be defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was mostly wrestling with TypeScript. I had to change the Redux state type for workspace.data.settings
to be optional (because it really is optional now), so the getWorkspace's return value is now typed as CustomizableWorkspace instead of Workspace, marking these parameters optional. I'll investigate for a workaround
truncateNumbersWithPound?: boolean; | ||
saveSettings: (settings: Partial<WorkspaceSettings>) => void; | ||
defaultDecimalFormatOptions?: FormatOption; | ||
defaultIntegerFormatOptions?: FormatOption; | ||
defaults: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defaults
we should be able to just map to getDefaultWorkspaceSettings
instead of redefining defaults here.
assertNotNull(showTimeZone); | ||
assertNotNull(showTSeparator); | ||
assertNotNull(timeZone); | ||
assertNotNull(truncateNumbersWithPound); | ||
assertNotNull(defaultDateTimeFormat); | ||
assertNotNull(defaultDecimalFormatOptions); | ||
assertNotNull(defaultIntegerFormatOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these should be optional/you shouldn't need to assert against null
IntegerColumnFormatter.makeCustomFormat(event.target.value) | ||
) | ||
) { | ||
this.commitChanges(update); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should still debounce these changes, as they could have implications for the entire UI (e.g. if you have a table open and you're updating the formatting, it'll update every cell of every table open with every keystroke).
See my other comment in this file about debouncing for a suggestion.
This is the
the only thing of importance is the integerFormat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still a few issues with functionality, I think I've pointed out all the problematic areas but here is a test case that I think should be comprehensive to illustrate what the issue is. This assumes starting from an incognito tab, so no previous workspace data
- Set
integerFormat=##000
in props - Login as user and check in the Settings menu
- Integer format option should show
##000
and not have the "Reset Integer Format" button visible, since we are using the default value
- Shut down the server, set
integerFormat=##111
in props - From the same browser as step 2 (so workspace data is intact), login and check the settings tab
- Integer format option should show
##111
and not have the "Reset Integer Format" visible
- Change the option to
##999
. Reset button should become visible - Shut down the server, set
integerFormat=##222
in props - Login and check settings tab
- Integer format should show
##999
as you set in step 5. Reset button should be visible
- Click the Reset button - the integer format should change to
##222
and reset button should disappear.
You can also inspect what you have set in Redux using the Redux dev tools, and what's stored in LocalStorage using the browser dev tools. You want to make sure the default settings in redux are populated with the settings read from the server (anything not defined by the server should fallback to whatever previous defaults we have set right now), and workspace storage settings should only have the options the user has changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor cleanup, but looks great! Thanks
Updated the follow values to use ServerConfig when possible:
To test, first create a config file (see https://deephaven.io/core/docs/how-to-guides/configuration/config-file/), then add
- ./deephaven.prop:/opt/deephaven/config/deephaven.prop
as a volumn for services.deephaven.columns
BREAKING CHANGE: saveSettings action has been replaced with
updateSettings
, which now just takes partial settings instead