@@ -3,6 +3,10 @@ import * as React from 'react';
3
3
import { action , observable , reaction , autorun , observe , runInAction , computed } from 'mobx' ;
4
4
import { observer , disposeOnUnmount , inject } from 'mobx-react' ;
5
5
import * as dedent from 'dedent' ;
6
+ import {
7
+ Operation as JsonPatchOperation ,
8
+ validate as validateJsonPatch
9
+ } from 'fast-json-patch' ;
6
10
7
11
import { Headers , RawHeaders } from '../../types' ;
8
12
import { css , styled } from '../../styles' ;
@@ -23,6 +27,10 @@ import {
23
27
HEADER_NAME_REGEX ,
24
28
setHeaderValue
25
29
} from '../../util/headers' ;
30
+ import {
31
+ ADVANCED_PATCH_TRANSFORMS ,
32
+ serverSupports
33
+ } from '../../services/service-versions' ;
26
34
27
35
import {
28
36
Handler ,
@@ -1105,7 +1113,8 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1105
1113
private static readonly FIELDS = [
1106
1114
'replaceBody' ,
1107
1115
'replaceBodyFromFile' ,
1108
- 'updateJsonBody'
1116
+ 'updateJsonBody' ,
1117
+ 'patchJsonBody'
1109
1118
] as const ;
1110
1119
1111
1120
@computed
@@ -1120,9 +1129,12 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1120
1129
onTransformTypeChange,
1121
1130
setBodyReplacement,
1122
1131
selectBodyReplacementFile,
1123
- setJsonBodyUpdate
1132
+ setJsonBodyUpdate,
1133
+ setJsonBodyPatch
1124
1134
} = this ;
1125
1135
1136
+ const advancedPatchesSupported = serverSupports ( ADVANCED_PATCH_TRANSFORMS ) ;
1137
+
1126
1138
const selected = _ . find ( BodyTransformConfig . FIELDS , ( field ) =>
1127
1139
transform [ field ] !== undefined
1128
1140
) ?? 'none' ;
@@ -1134,7 +1146,10 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1134
1146
< option value = 'none' > Pass through the real { type } body</ option >
1135
1147
< option value = 'replaceBody' > Replace the { type } body with a fixed value</ option >
1136
1148
< option value = 'replaceBodyFromFile' > Replace the { type } body with a file</ option >
1137
- < option value = 'updateJsonBody' > Update values within a JSON { type } body</ option >
1149
+ < option value = 'updateJsonBody' > Update a JSON { type } body by merging data</ option >
1150
+ { advancedPatchesSupported && < >
1151
+ < option value = 'patchJsonBody' > Update a JSON { type } body using JSON patch</ option >
1152
+ </ > }
1138
1153
</ SelectTransform >
1139
1154
{
1140
1155
selected === 'replaceBody'
@@ -1165,7 +1180,15 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1165
1180
body = { transform . updateJsonBody ! }
1166
1181
updateBody = { setJsonBodyUpdate }
1167
1182
/>
1168
- : null
1183
+ : selected === 'patchJsonBody'
1184
+ ? < JsonPatchTransformConfig
1185
+ type = { type }
1186
+ operations = { transform . patchJsonBody ! }
1187
+ updateOperations = { setJsonBodyPatch }
1188
+ />
1189
+ : selected === 'none'
1190
+ ? null
1191
+ : unreachableCheck ( selected )
1169
1192
}
1170
1193
</ TransformConfig > ;
1171
1194
}
@@ -1176,11 +1199,15 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1176
1199
this . clearValues ( ) ;
1177
1200
if ( value === 'updateJsonBody' ) {
1178
1201
this . props . onChange ( 'updateJsonBody' ) ( { } ) ;
1202
+ } else if ( value === 'patchJsonBody' ) {
1203
+ this . props . onChange ( 'patchJsonBody' ) ( [ ] ) ;
1179
1204
} else if ( value === 'replaceBody' ) {
1180
1205
this . props . onChange ( 'replaceBody' ) ( '' ) ;
1181
1206
} else if ( value === 'replaceBodyFromFile' ) {
1182
1207
this . props . onChange ( 'replaceBodyFromFile' ) ( '' ) ;
1183
- }
1208
+ } else if ( value === 'none' ) {
1209
+ return ;
1210
+ } else unreachableCheck ( value ) ;
1184
1211
} ;
1185
1212
1186
1213
@action . bound
@@ -1211,6 +1238,12 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
1211
1238
this . clearValues ( ) ;
1212
1239
this . props . onChange ( 'updateJsonBody' ) ( body ) ;
1213
1240
} ;
1241
+
1242
+ @action . bound
1243
+ setJsonBodyPatch ( operations : Array < JsonPatchOperation > ) {
1244
+ this . clearValues ( ) ;
1245
+ this . props . onChange ( 'patchJsonBody' ) ( operations ) ;
1246
+ } ;
1214
1247
} ;
1215
1248
1216
1249
const RawBodyTransfomConfig = ( props : {
@@ -1276,7 +1309,7 @@ const JsonUpdateTransformConfig = (props: {
1276
1309
1277
1310
return < TransformDetails >
1278
1311
< BodyHeader >
1279
- < SectionLabel > JSON { props . type } body patch </ SectionLabel >
1312
+ < SectionLabel > JSON to merge into { props . type } body</ SectionLabel >
1280
1313
{ error && < WarningIcon title = { error . message } /> }
1281
1314
1282
1315
< StandaloneFormatButton
@@ -1296,6 +1329,55 @@ const JsonUpdateTransformConfig = (props: {
1296
1329
</ TransformDetails > ;
1297
1330
} ;
1298
1331
1332
+ const JsonPatchTransformConfig = ( props : {
1333
+ type : 'request' | 'response' ,
1334
+ operations : Array < JsonPatchOperation > ,
1335
+ updateOperations : ( operations : Array < JsonPatchOperation > ) => void
1336
+ } ) => {
1337
+ const [ error , setError ] = React . useState < Error > ( ) ;
1338
+
1339
+ const [ operationsString , setOperationsString ] = React . useState < string > (
1340
+ JSON . stringify ( props . operations , null , 2 )
1341
+ ) ;
1342
+
1343
+ React . useEffect ( ( ) => {
1344
+ try {
1345
+ const parsedInput = JSON . parse ( operationsString ) ;
1346
+
1347
+ const validationError = validateJsonPatch ( parsedInput ) ;
1348
+ if ( validationError ) throw validationError ;
1349
+
1350
+ props . updateOperations ( parsedInput ) ;
1351
+ setError ( undefined ) ;
1352
+ } catch ( e ) {
1353
+ setError ( asError ( e ) ) ;
1354
+ }
1355
+ } , [ operationsString ] ) ;
1356
+
1357
+ return < TransformDetails >
1358
+ < BodyHeader >
1359
+ < SectionLabel > JSON { props . type } body patch (see < a
1360
+ href = "https://jsonpatch.com/"
1361
+ > jsonpatch.com</ a > )</ SectionLabel >
1362
+ { error && < WarningIcon title = { error . message } /> }
1363
+
1364
+ < StandaloneFormatButton
1365
+ format = 'json'
1366
+ content = { asBuffer ( operationsString ) }
1367
+ onFormatted = { setOperationsString }
1368
+ />
1369
+ </ BodyHeader >
1370
+ < BodyContainer >
1371
+ < SelfSizedEditor
1372
+ contentId = { null }
1373
+ language = 'json'
1374
+ value = { operationsString }
1375
+ onChange = { ( content ) => setOperationsString ( content ) }
1376
+ />
1377
+ </ BodyContainer >
1378
+ </ TransformDetails > ;
1379
+ } ;
1380
+
1299
1381
@observer
1300
1382
class PassThroughHandlerConfig extends HandlerConfig <
1301
1383
| PassThroughHandler
0 commit comments