@@ -3,6 +3,10 @@ import * as React from 'react';
33import { action , observable , reaction , autorun , observe , runInAction , computed } from 'mobx' ;
44import { observer , disposeOnUnmount , inject } from 'mobx-react' ;
55import * as dedent from 'dedent' ;
6+ import {
7+ Operation as JsonPatchOperation ,
8+ validate as validateJsonPatch
9+ } from 'fast-json-patch' ;
610
711import { Headers , RawHeaders } from '../../types' ;
812import { css , styled } from '../../styles' ;
@@ -23,6 +27,10 @@ import {
2327 HEADER_NAME_REGEX ,
2428 setHeaderValue
2529} from '../../util/headers' ;
30+ import {
31+ ADVANCED_PATCH_TRANSFORMS ,
32+ serverSupports
33+ } from '../../services/service-versions' ;
2634
2735import {
2836 Handler ,
@@ -1105,7 +1113,8 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
11051113 private static readonly FIELDS = [
11061114 'replaceBody' ,
11071115 'replaceBodyFromFile' ,
1108- 'updateJsonBody'
1116+ 'updateJsonBody' ,
1117+ 'patchJsonBody'
11091118 ] as const ;
11101119
11111120 @computed
@@ -1120,9 +1129,12 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
11201129 onTransformTypeChange,
11211130 setBodyReplacement,
11221131 selectBodyReplacementFile,
1123- setJsonBodyUpdate
1132+ setJsonBodyUpdate,
1133+ setJsonBodyPatch
11241134 } = this ;
11251135
1136+ const advancedPatchesSupported = serverSupports ( ADVANCED_PATCH_TRANSFORMS ) ;
1137+
11261138 const selected = _ . find ( BodyTransformConfig . FIELDS , ( field ) =>
11271139 transform [ field ] !== undefined
11281140 ) ?? 'none' ;
@@ -1134,7 +1146,10 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
11341146 < option value = 'none' > Pass through the real { type } body</ option >
11351147 < option value = 'replaceBody' > Replace the { type } body with a fixed value</ option >
11361148 < 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+ </ > }
11381153 </ SelectTransform >
11391154 {
11401155 selected === 'replaceBody'
@@ -1165,7 +1180,15 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
11651180 body = { transform . updateJsonBody ! }
11661181 updateBody = { setJsonBodyUpdate }
11671182 />
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 )
11691192 }
11701193 </ TransformConfig > ;
11711194 }
@@ -1176,11 +1199,15 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
11761199 this . clearValues ( ) ;
11771200 if ( value === 'updateJsonBody' ) {
11781201 this . props . onChange ( 'updateJsonBody' ) ( { } ) ;
1202+ } else if ( value === 'patchJsonBody' ) {
1203+ this . props . onChange ( 'patchJsonBody' ) ( [ ] ) ;
11791204 } else if ( value === 'replaceBody' ) {
11801205 this . props . onChange ( 'replaceBody' ) ( '' ) ;
11811206 } else if ( value === 'replaceBodyFromFile' ) {
11821207 this . props . onChange ( 'replaceBodyFromFile' ) ( '' ) ;
1183- }
1208+ } else if ( value === 'none' ) {
1209+ return ;
1210+ } else unreachableCheck ( value ) ;
11841211 } ;
11851212
11861213 @action . bound
@@ -1211,6 +1238,12 @@ class BodyTransformConfig<T extends RequestTransform | ResponseTransform> extend
12111238 this . clearValues ( ) ;
12121239 this . props . onChange ( 'updateJsonBody' ) ( body ) ;
12131240 } ;
1241+
1242+ @action . bound
1243+ setJsonBodyPatch ( operations : Array < JsonPatchOperation > ) {
1244+ this . clearValues ( ) ;
1245+ this . props . onChange ( 'patchJsonBody' ) ( operations ) ;
1246+ } ;
12141247} ;
12151248
12161249const RawBodyTransfomConfig = ( props : {
@@ -1276,7 +1309,7 @@ const JsonUpdateTransformConfig = (props: {
12761309
12771310 return < TransformDetails >
12781311 < BodyHeader >
1279- < SectionLabel > JSON { props . type } body patch </ SectionLabel >
1312+ < SectionLabel > JSON to merge into { props . type } body</ SectionLabel >
12801313 { error && < WarningIcon title = { error . message } /> }
12811314
12821315 < StandaloneFormatButton
@@ -1296,6 +1329,55 @@ const JsonUpdateTransformConfig = (props: {
12961329 </ TransformDetails > ;
12971330} ;
12981331
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+
12991381@observer
13001382class PassThroughHandlerConfig extends HandlerConfig <
13011383 | PassThroughHandler
0 commit comments