1515 */
1616
1717import { Icon } from '@iconify/react' ;
18+ import { compare } from 'fast-json-patch' ;
1819import React from 'react' ;
1920import { useTranslation } from 'react-i18next' ;
2021import { useDispatch } from 'react-redux' ;
2122import { useLocation } from 'react-router-dom' ;
23+ import { patch } from '../../../lib/k8s/api/v1/clusterRequests' ;
24+ import { KubeObjectEndpoint } from '../../../lib/k8s/api/v2/KubeObjectEndpoint' ;
2225import { KubeObject } from '../../../lib/k8s/KubeObject' ;
2326import { KubeObjectInterface } from '../../../lib/k8s/KubeObject' ;
2427import { CallbackActionOptions , clusterAction } from '../../../redux/clusterActionSlice' ;
@@ -51,6 +54,10 @@ export default function EditButton(props: EditButtonProps) {
5154 const dispatchHeadlampEditEvent = useEventCallback ( HeadlampEventType . EDIT_RESOURCE ) ;
5255 const activityId = 'edit-' + item . metadata . uid ;
5356
57+ // Store the original resource snapshot (firstDraft) for JSON Patch comparison
58+ // This is set when the editor is opened
59+ const originalResourceRef = React . useRef < any > ( null ) ;
60+
5461 function makeErrorMessage ( err : any ) {
5562 const status : number = err . status ;
5663 switch ( status ) {
@@ -63,7 +70,41 @@ export default function EditButton(props: EditButtonProps) {
6370
6471 async function updateFunc ( newItem : KubeObjectInterface ) {
6572 try {
66- await item . update ( newItem ) ;
73+ if ( ! originalResourceRef . current ) {
74+ throw new Error ( 'Original resource snapshot not found' ) ;
75+ }
76+
77+ // Calculate JSON Patch: diff between original (when editor opened) and new (user edited)
78+ const patches = compare ( originalResourceRef . current , newItem ) ;
79+
80+ if ( patches . length === 0 ) {
81+ // No changes detected
82+ Activity . close ( activityId ) ;
83+ return ;
84+ }
85+
86+ // Build the API URL
87+ const apiInfo = item . _class ( ) . apiEndpoint . apiInfo [ 0 ] ;
88+ const endpoint : KubeObjectEndpoint = {
89+ group : apiInfo . group ,
90+ version : apiInfo . version ,
91+ resource : apiInfo . resource ,
92+ } ;
93+
94+ const namespace = item . getNamespace ( ) ;
95+ const name = item . getName ( ) ;
96+ const urlParts = [ KubeObjectEndpoint . toUrl ( endpoint , namespace ) , name ] ;
97+ const url = urlParts . filter ( Boolean ) . join ( '/' ) ;
98+
99+ // Use the patch function with JSON Patch content type
100+ // Override the default 'application/merge-patch+json' to 'application/json-patch+json'
101+ await patch ( url , patches , true , {
102+ cluster : item . _clusterName ,
103+ headers : {
104+ 'Content-Type' : 'application/json-patch+json' ,
105+ } ,
106+ } ) ;
107+
67108 Activity . close ( activityId ) ;
68109 } catch ( err ) {
69110 Activity . update ( activityId , { minimized : false } ) ;
@@ -128,6 +169,9 @@ export default function EditButton(props: EditButtonProps) {
128169 if ( afterConfirm ) {
129170 afterConfirm ( ) ;
130171 }
172+ const editableObject = item . getEditableObject ( ) ;
173+ originalResourceRef . current = editableObject ;
174+
131175 Activity . launch ( {
132176 id : activityId ,
133177 title : t ( 'translation|Edit' ) + ': ' + item . metadata . name ,
@@ -136,7 +180,7 @@ export default function EditButton(props: EditButtonProps) {
136180 content : (
137181 < EditorDialog
138182 noDialog
139- item = { item . getEditableObject ( ) }
183+ item = { editableObject }
140184 open
141185 onClose = { ( ) => Activity . close ( activityId ) }
142186 onSave = { handleSave }
0 commit comments