From 9716feb09c6a969fe661a2ad28675c98fd685ba8 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Fri, 13 Dec 2024 20:22:50 -0500 Subject: [PATCH 1/5] feat: improve StatefulSet immutable field error messages Signed-off-by: Atif Ali --- .../application-operation-state.tsx | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx index a76ecca005bfe..f63ac4904e071 100644 --- a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx +++ b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx @@ -54,12 +54,52 @@ const Filter = (props: {filters: string[]; setFilters: (f: string[]) => void; op export const ApplicationOperationState: React.StatelessComponent = ({application, operationState}, ctx: AppContext) => { const [messageFilters, setMessageFilters] = React.useState([]); + const getFormattedMessage = (message: string) => { + const prefix = 'one or more objects failed to apply, reason: '; + let cleanMessage = message; + + // Remove duplicate prefix if exists + if (message.startsWith(prefix) && message.substring(prefix.length).startsWith(prefix)) { + cleanMessage = prefix + message.substring(prefix.length * 2); + } + + // Format immutable fields error message + if (cleanMessage.includes('attempting to change immutable fields:')) { + const [header, ...details] = cleanMessage.split('\n'); + const formattedDetails = details + .filter(line => line.trim()) + .map(line => { + if (line.startsWith('-')) { + const [field, changes] = line.substring(2).split(':'); + if (changes) { + const [from, to] = changes.split('to:').map(s => s.trim()); + return ` - ${field}:\n from: ${from.replace('from:', '').trim()}\n to: ${to}`; + } + } + return line; + }) + .join('\n'); + + return `${header}\n${formattedDetails}`; + } + + return cleanMessage; + }; const operationAttributes = [ {title: 'OPERATION', value: utils.getOperationType(application)}, {title: 'PHASE', value: operationState.phase}, - ...(operationState.message ? [{title: 'MESSAGE', value: operationState.message}] : []), - {title: 'STARTED AT', value: }, + ...(operationState.message ? [{ + title: 'MESSAGE', + value:
+                {getFormattedMessage(operationState.message)}
+            
+ }] : []), {title: 'STARTED AT', value: }, { title: 'DURATION', value: ( From 8fb18c6e5135ad9deca14a89f4010dc6a1d115f0 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 18 Dec 2024 15:13:17 -0500 Subject: [PATCH 2/5] ui lint Signed-off-by: Atif Ali --- .../application-operation-state.tsx | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx index f63ac4904e071..a7b63295a6f88 100644 --- a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx +++ b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx @@ -57,12 +57,12 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl const getFormattedMessage = (message: string) => { const prefix = 'one or more objects failed to apply, reason: '; let cleanMessage = message; - + // Remove duplicate prefix if exists if (message.startsWith(prefix) && message.substring(prefix.length).startsWith(prefix)) { cleanMessage = prefix + message.substring(prefix.length * 2); } - + // Format immutable fields error message if (cleanMessage.includes('attempting to change immutable fields:')) { const [header, ...details] = cleanMessage.split('\n'); @@ -79,27 +79,35 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl return line; }) .join('\n'); - + return `${header}\n${formattedDetails}`; } - + return cleanMessage; }; const operationAttributes = [ {title: 'OPERATION', value: utils.getOperationType(application)}, {title: 'PHASE', value: operationState.phase}, - ...(operationState.message ? [{ - title: 'MESSAGE', - value:
-                {getFormattedMessage(operationState.message)}
-            
- }] : []), {title: 'STARTED AT', value: }, + ...(operationState.message + ? [ + { + title: 'MESSAGE', + value: ( +
+                              {getFormattedMessage(operationState.message)}
+                          
+ ) + } + ] + : []), + {title: 'STARTED AT', value: }, { title: 'DURATION', value: ( From 3e0888c37930493794240755e156f858ce9ba81e Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 19 Dec 2024 16:44:29 -0500 Subject: [PATCH 3/5] move immuatble formatter utils.tsx Signed-off-by: Atif Ali --- .../application-operation-state.tsx | 33 +------------------ ui/src/app/applications/components/utils.tsx | 30 +++++++++++++++++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx index a7b63295a6f88..b4c8cd30f25ee 100644 --- a/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx +++ b/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx @@ -54,37 +54,6 @@ const Filter = (props: {filters: string[]; setFilters: (f: string[]) => void; op export const ApplicationOperationState: React.StatelessComponent = ({application, operationState}, ctx: AppContext) => { const [messageFilters, setMessageFilters] = React.useState([]); - const getFormattedMessage = (message: string) => { - const prefix = 'one or more objects failed to apply, reason: '; - let cleanMessage = message; - - // Remove duplicate prefix if exists - if (message.startsWith(prefix) && message.substring(prefix.length).startsWith(prefix)) { - cleanMessage = prefix + message.substring(prefix.length * 2); - } - - // Format immutable fields error message - if (cleanMessage.includes('attempting to change immutable fields:')) { - const [header, ...details] = cleanMessage.split('\n'); - const formattedDetails = details - .filter(line => line.trim()) - .map(line => { - if (line.startsWith('-')) { - const [field, changes] = line.substring(2).split(':'); - if (changes) { - const [from, to] = changes.split('to:').map(s => s.trim()); - return ` - ${field}:\n from: ${from.replace('from:', '').trim()}\n to: ${to}`; - } - } - return line; - }) - .join('\n'); - - return `${header}\n${formattedDetails}`; - } - - return cleanMessage; - }; const operationAttributes = [ {title: 'OPERATION', value: utils.getOperationType(application)}, @@ -101,7 +70,7 @@ export const ApplicationOperationState: React.StatelessComponent = ({appl margin: 0, fontFamily: 'inherit' }}> - {getFormattedMessage(operationState.message)} + {utils.formatOperationMessage(operationState.message)} ) } diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index ba690865ce4cc..eea4dad0f579b 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -1547,6 +1547,36 @@ export function formatCreationTimestamp(creationTimestamp: string) { ); } +export function formatOperationMessage(message: string): string { + if (!message) { + return message; + } + + let cleanMessage = message; + + // Format immutable fields error message + if (cleanMessage.includes('attempting to change immutable fields:')) { + const [header, ...details] = cleanMessage.split('\n'); + const formattedDetails = details + .filter(line => line.trim()) + .map(line => { + if (line.startsWith('-')) { + const [field, changes] = line.substring(2).split(':'); + if (changes) { + const [from, to] = changes.split('to:').map(s => s.trim()); + return ` - ${field}:\n from: ${from.replace('from:', '').trim()}\n to: ${to}`; + } + } + return line; + }) + .join('\n'); + + return `${header}\n${formattedDetails}`; + } + + return cleanMessage; +} + export const selectPostfix = (arr: string[], singular: string, plural: string) => (arr.length > 1 ? plural : singular); export function getUsrMsgKeyToDisplay(appName: string, msgKey: string, usrMessages: appModels.UserMessages[]) { From ef84556a699ccb413c1afdb0ffaab752f37ee546 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Thu, 19 Dec 2024 17:28:50 -0500 Subject: [PATCH 4/5] linter fix Signed-off-by: Atif Ali --- ui/src/app/applications/components/utils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index eea4dad0f579b..0642505dbb459 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -1552,7 +1552,7 @@ export function formatOperationMessage(message: string): string { return message; } - let cleanMessage = message; + const cleanMessage = message; // Format immutable fields error message if (cleanMessage.includes('attempting to change immutable fields:')) { From dbf0f8456f99bbb6dea45ffe0a171bbccc73432e Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 26 Feb 2025 15:45:01 -0500 Subject: [PATCH 5/5] Extracted the immutable field formatting logic into a separate function && add comments Signed-off-by: Atif Ali --- ui/src/app/applications/components/utils.tsx | 38 ++++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index 0642505dbb459..3f599dc07e452 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -1547,34 +1547,42 @@ export function formatCreationTimestamp(creationTimestamp: string) { ); } +/* + * formatStatefulSetChange reformats a single line describing changes to immutable fields in a StatefulSet. + * It extracts the field name and its "from" and "to" values for better readability. + */ +function formatStatefulSetChange(line: string): string { + if (line.startsWith('-')) { + // Remove leading "- " from the line and split into field and changes + const [field, changes] = line.substring(2).split(':'); + if (changes) { + // Split "from: X to: Y" into separate lines with aligned values + const [from, to] = changes.split('to:').map(s => s.trim()); + return ` - ${field}:\n from: ${from.replace('from:', '').trim()}\n to: ${to}`; + } + } + return line; +} + export function formatOperationMessage(message: string): string { if (!message) { return message; } - const cleanMessage = message; - // Format immutable fields error message - if (cleanMessage.includes('attempting to change immutable fields:')) { - const [header, ...details] = cleanMessage.split('\n'); + if (message.includes('attempting to change immutable fields:')) { + const [header, ...details] = message.split('\n'); const formattedDetails = details + // Remove empty lines .filter(line => line.trim()) - .map(line => { - if (line.startsWith('-')) { - const [field, changes] = line.substring(2).split(':'); - if (changes) { - const [from, to] = changes.split('to:').map(s => s.trim()); - return ` - ${field}:\n from: ${from.replace('from:', '').trim()}\n to: ${to}`; - } - } - return line; - }) + // Use helper function + .map(formatStatefulSetChange) .join('\n'); return `${header}\n${formattedDetails}`; } - return cleanMessage; + return message; } export const selectPostfix = (arr: string[], singular: string, plural: string) => (arr.length > 1 ? plural : singular);