Skip to content
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: Add Cloud Config change history to roll back to previous values #2554

Merged
merged 21 commits into from
May 19, 2024
Merged
39 changes: 38 additions & 1 deletion src/dashboard/Data/Config/Config.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import TableHeader from 'components/Table/TableHeader.react';
import TableView from 'dashboard/TableView.react';
import Toolbar from 'components/Toolbar/Toolbar.react';
import browserStyles from 'dashboard/Data/Browser/Browser.scss';
import { CurrentApp } from 'context/currentApp';

@subscribeTo('Config', 'config')
class Config extends TableView {
static contextType = CurrentApp;
constructor() {
super();
this.section = 'Core';
Expand Down Expand Up @@ -242,7 +244,7 @@ class Config extends TableView {
return data;
}

saveParam({ name, value, masterKeyOnly }) {
saveParam({ name, value, type, masterKeyOnly }) {
this.props.config
.dispatch(ActionTypes.SET, {
param: name,
Expand All @@ -252,6 +254,32 @@ class Config extends TableView {
.then(
() => {
this.setState({ modalOpen: false });
const limit = this.context.cloudConfigHistoryLimit;
const applicationId = this.context.applicationId;
let transformedValue = value;
if(type === 'Date') {
transformedValue = {__type: 'Date', iso: value};
}
if(type === 'File') {
transformedValue = {name: value._name, url: value._url};
}
const configHistory = localStorage.getItem(`${applicationId}_configHistory`);
if(!configHistory) {
localStorage.setItem(`${applicationId}_configHistory`, JSON.stringify({
[name]: [{
time: new Date(),
value: transformedValue
}]
}));
} else {
const oldConfigHistory = JSON.parse(configHistory);
localStorage.setItem(`${applicationId}_configHistory`, JSON.stringify({
...oldConfigHistory,
[name]: !oldConfigHistory[name] ?
[{time: new Date(), value: transformedValue}]
: [{time: new Date(), value: transformedValue}, ...oldConfigHistory[name]].slice(0, limit || 100)
}));
}
},
() => {
// Catch the error
Expand All @@ -263,6 +291,15 @@ class Config extends TableView {
this.props.config.dispatch(ActionTypes.DELETE, { param: name }).then(() => {
this.setState({ showDeleteParameterDialog: false });
});
const configHistory = localStorage.getItem('configHistory') && JSON.parse(localStorage.getItem('configHistory'));
if(configHistory) {
delete configHistory[name];
if(Object.keys(configHistory).length === 0) {
localStorage.removeItem('configHistory');
} else {
localStorage.setItem('configHistory', JSON.stringify(configHistory));
}
}
}

createParameter() {
Expand Down
51 changes: 51 additions & 0 deletions src/dashboard/Data/Config/ConfigDialog.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import Toggle from 'components/Toggle/Toggle.react';
import validateNumeric from 'lib/validateNumeric';
import styles from 'dashboard/Data/Browser/Browser.scss';
import semver from 'semver/preload.js';
import { dateStringUTC } from 'lib/DateUtils';
import { CurrentApp } from 'context/currentApp';

const PARAM_TYPES = ['Boolean', 'String', 'Number', 'Date', 'Object', 'Array', 'GeoPoint', 'File'];

Expand Down Expand Up @@ -90,20 +92,23 @@ const GET_VALUE = {
};

export default class ConfigDialog extends React.Component {
static contextType = CurrentApp;
constructor(props) {
super();
this.state = {
value: null,
type: 'String',
name: '',
masterKeyOnly: false,
selectedIndex: null,
};
if (props.param.length > 0) {
this.state = {
name: props.param,
type: props.type,
value: props.value,
masterKeyOnly: props.masterKeyOnly,
selectedIndex: 0,
};
}
}
Expand Down Expand Up @@ -169,6 +174,7 @@ export default class ConfigDialog extends React.Component {
submit() {
this.props.onConfirm({
name: this.state.name,
type: this.state.type,
value: GET_VALUE[this.state.type](this.state.value),
masterKeyOnly: this.state.masterKeyOnly,
});
Expand All @@ -190,6 +196,28 @@ export default class ConfigDialog extends React.Component {
))}
</Dropdown>
);
const configHistory = localStorage.getItem(`${this.context.applicationId}_configHistory`) && JSON.parse(localStorage.getItem(`${this.context.applicationId}_configHistory`))[this.state.name];
const handleIndexChange = index => {
if(this.state.type === 'Date'){
return;
}
let value = configHistory[index].value;
if(this.state.type === 'File'){
const fileJSON = {
__type: 'File',
name: value.name,
url: value.url
};
const file = Parse.File.fromJSON(fileJSON);
this.setState({ selectedIndex: index, value: file });
return;
}
if(typeof value === 'object'){
value = JSON.stringify(value);
}
this.setState({ selectedIndex: index, value });
};

return (
<Modal
type={Modal.Types.INFO}
Expand Down Expand Up @@ -253,6 +281,29 @@ export default class ConfigDialog extends React.Component {
/>
) : null
}
{
configHistory?.length > 0 &&
<Field
label={
<Label
text="Change History"
description="Select a timestamp in the change history to preview the value in the 'Value' field before saving."
/>
}
input={
<Dropdown
value={this.state.selectedIndex}
onChange={handleIndexChange}>
{configHistory.map((value, i) =>
<Option key={i} value={i}>
{dateStringUTC(new Date(value.time))}
</Option>
)}
</Dropdown>
}
className={styles.addColumnToggleWrapper}
/>
}
</Modal>
);
}
Expand Down
4 changes: 3 additions & 1 deletion src/lib/ParseApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export default class ParseApp {
columnPreference,
scripts,
classPreference,
enableSecurityChecks
enableSecurityChecks,
cloudConfigHistoryLimit
}) {
this.name = appName;
this.createdAt = created_at ? new Date(created_at) : new Date();
Expand Down Expand Up @@ -77,6 +78,7 @@ export default class ParseApp {
this.columnPreference = columnPreference;
this.scripts = scripts;
this.enableSecurityChecks = !!enableSecurityChecks;
this.cloudConfigHistoryLimit = cloudConfigHistoryLimit;

if (!supportedPushLocales) {
console.warn(
Expand Down
Loading