Skip to content

Commit 551bb4c

Browse files
committed
Save only the non-default attributes
Signed-off-by: David Gageot <[email protected]>
1 parent 8da4f01 commit 551bb4c

File tree

1 file changed

+60
-50
lines changed

1 file changed

+60
-50
lines changed

src/extension/ui/src/components/tile/ConfigEditor.tsx

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { v1 } from '@docker/extension-api-client-types';
22
import CheckOutlined from '@mui/icons-material/CheckOutlined';
33
import CloseOutlined from '@mui/icons-material/CloseOutlined';
44
import {
5-
CircularProgress,
65
IconButton,
76
Stack,
87
TextField,
@@ -42,7 +41,6 @@ const ConfigEditor = ({
4241
const [localConfig, setLocalConfig] = useState<
4342
{ [key: string]: any } | undefined
4443
>(undefined);
45-
const [savingKeys, setSavingKeys] = useState<Set<string>>(new Set());
4644

4745
// Use memoized flattenedConfig to ensure it only updates when config changes
4846
// This MUST be called before any early returns to avoid conditional hook calls
@@ -86,7 +84,6 @@ const ConfigEditor = ({
8684
<Stack>
8785
{Object.keys(flattenedConfig).map((key: string) => {
8886
const edited = localConfig[key] !== flattenedConfig[key];
89-
const isSaving = savingKeys.has(key);
9087

9188
return (
9289
<Stack
@@ -105,54 +102,40 @@ const ConfigEditor = ({
105102
onChange={(e) =>
106103
setLocalConfig({ ...localConfig, [key]: e.target.value })
107104
}
108-
disabled={isSaving}
109105
/>
110106
{edited && (
111-
<>
112-
{isSaving ? (
113-
<CircularProgress size={24} />
114-
) : (
115-
<Stack direction="row" spacing={1}>
116-
<IconButton
117-
size="small"
118-
onClick={() => {
119-
const newConfig = buildObjectFromFlattenedObject(localConfig);
120-
121-
// Remove all attributes which are optional and which have the defautl value
122-
const schema = new JsonSchemaLibrary.Draft2019(catalogItem.config[0]);
123-
const requiredAttributes = (schema.rootSchema.required || []) as string[];
124-
const template = schema.getTemplate({});
125-
const requiredConfig = Object.fromEntries(Object.entries(newConfig).filter(([key, value]) => {
126-
return requiredAttributes.includes(key) || (value !== template[key]);
127-
}));
128-
129-
updateExistingConfig(catalogItem.name, requiredConfig)
130-
}}
131-
disabled={isSaving}
132-
>
133-
<CheckOutlined
134-
fontSize="small"
135-
sx={{ color: 'success.main' }}
136-
/>
137-
</IconButton>
138-
<IconButton
139-
size="small"
140-
onClick={() =>
141-
setLocalConfig({
142-
...localConfig,
143-
[key]: flattenedConfig[key],
144-
})
145-
}
146-
disabled={isSaving}
147-
>
148-
<CloseOutlined
149-
fontSize="small"
150-
sx={{ color: 'error.main' }}
151-
/>
152-
</IconButton>
153-
</Stack>
154-
)}
155-
</>
107+
<Stack direction="row" spacing={1}>
108+
<IconButton
109+
size="small"
110+
onClick={() => {
111+
const newConfig = sanitizeConfig(localConfig, catalogItem);
112+
updateExistingConfig(catalogItem.name, newConfig);
113+
setLocalConfig({
114+
...localConfig,
115+
[key]: newConfig[key],
116+
});
117+
}}
118+
>
119+
<CheckOutlined
120+
fontSize="small"
121+
sx={{ color: 'success.main' }}
122+
/>
123+
</IconButton>
124+
<IconButton
125+
size="small"
126+
onClick={() =>
127+
setLocalConfig({
128+
...localConfig,
129+
[key]: flattenedConfig[key],
130+
})
131+
}
132+
>
133+
<CloseOutlined
134+
fontSize="small"
135+
sx={{ color: 'error.main' }}
136+
/>
137+
</IconButton>
138+
</Stack>
156139
)}
157140
</Stack>
158141
);
@@ -162,4 +145,31 @@ const ConfigEditor = ({
162145
);
163146
};
164147

165-
export default ConfigEditor;
148+
function sanitizeConfig(config: { [key: string]: any; }, catalogItem: CatalogItemRichened) {
149+
const newConfig = buildObjectFromFlattenedObject(config);
150+
151+
// Remove all attributes which are optional and which have the defautl value
152+
const schema = new JsonSchemaLibrary.Draft2019(catalogItem.config[0]);
153+
const requiredAttributes = (schema.rootSchema.required || []) as string[];
154+
const template = schema.getTemplate({});
155+
const requiredConfig = Object.fromEntries(Object.entries(newConfig).filter(([key, value]) => {
156+
return requiredAttributes.includes(key) || (value !== template[key]);
157+
}));
158+
159+
// Use the right types for each attribute
160+
const typedConfig = Object.fromEntries(Object.entries(requiredConfig).map(([key, value]) => {
161+
const propertyType = schema.rootSchema.properties[key].type;
162+
switch (propertyType) {
163+
case "integer":
164+
return [key, parseInt(value) || 0];
165+
case "boolean":
166+
return [key, (value as string).toLowerCase() === "true"];
167+
default:
168+
return [key, value];
169+
}
170+
}));
171+
172+
return typedConfig;
173+
}
174+
175+
export default ConfigEditor;

0 commit comments

Comments
 (0)