Skip to content

Commit c57b32d

Browse files
committed
feat: add value length cap to partialClone
1 parent d6d839a commit c57b32d

File tree

6 files changed

+50
-14
lines changed

6 files changed

+50
-14
lines changed

packages/amazonq/test/e2e/inline/inline.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ describe('Amazon Q Inline', async function () {
122122
.query({
123123
metricName: 'codewhisperer_userTriggerDecision',
124124
})
125-
.map((e) => collectionUtil.partialClone(e, 3, ['credentialStartUrl'], '[omitted]'))
125+
.map((e) => collectionUtil.partialClone(e, 3, ['credentialStartUrl'], { replacement: '[omitted]' }))
126126
}
127127

128128
for (const [name, invokeCompletion] of [

packages/core/src/auth/sso/clients.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ function addLoggingMiddleware(client: SSOOIDCClient) {
258258
args.input as unknown as Record<string, unknown>,
259259
3,
260260
['clientSecret', 'accessToken', 'refreshToken'],
261-
'[omitted]'
261+
{ replacement: '[omitted]' }
262262
)
263263
getLogger().debug('API request (%s %s): %O', hostname, path, input)
264264
}
@@ -288,7 +288,7 @@ function addLoggingMiddleware(client: SSOOIDCClient) {
288288
result.output as unknown as Record<string, unknown>,
289289
3,
290290
['clientSecret', 'accessToken', 'refreshToken'],
291-
'[omitted]'
291+
{ replacement: '[omitted]' }
292292
)
293293
getLogger().debug('API response (%s %s): %O', hostname, path, output)
294294
}

packages/core/src/shared/utilities/collectionUtils.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { isWeb } from '../extensionGlobals'
77
import { inspect as nodeInspect } from 'util'
88
import { AsyncCollection, toCollection } from './asyncCollection'
99
import { SharedProp, AccumulableKeys, Coalesce, isNonNullable } from './tsUtils'
10+
import { truncate } from './textUtilities'
1011

1112
export function union<T>(a: Iterable<T>, b: Iterable<T>): Set<T> {
1213
const result = new Set<T>()
@@ -304,26 +305,36 @@ export function assign<T extends Record<any, any>, U extends Partial<T>>(data: T
304305
* @param depth
305306
* @param omitKeys Omit properties matching these names (at any depth).
306307
* @param replacement Replacement for object whose fields extend beyond `depth`, and properties matching `omitKeys`.
308+
* @param maxLength truncates string values that exceed this threshold.
307309
*/
308-
export function partialClone(obj: any, depth: number = 3, omitKeys: string[] = [], replacement?: any): any {
310+
export function partialClone(
311+
obj: any,
312+
depth: number = 3,
313+
omitKeys: string[] = [],
314+
options?: {
315+
replacement?: any
316+
maxLength?: number
317+
}
318+
): any {
309319
// Base case: If input is not an object or has no children, return it.
310320
if (typeof obj !== 'object' || obj === null || 0 === Object.getOwnPropertyNames(obj).length) {
311-
return obj
321+
const maxLength = options?.maxLength
322+
return typeof obj === 'string' && maxLength !== undefined ? truncate(obj, maxLength, '...') : obj
312323
}
313324

314325
// Create a new object of the same type as the input object.
315326
const clonedObj = Array.isArray(obj) ? [] : {}
316327

317328
if (depth === 0) {
318-
return replacement ? replacement : clonedObj
329+
return options?.replacement ? options.replacement : clonedObj
319330
}
320331

321332
// Recursively clone properties of the input object
322333
for (const key in obj) {
323334
if (omitKeys.includes(key)) {
324-
;(clonedObj as any)[key] = replacement ? replacement : Array.isArray(obj) ? [] : {}
335+
;(clonedObj as any)[key] = options?.replacement ? options.replacement : Array.isArray(obj) ? [] : {}
325336
} else if (Object.prototype.hasOwnProperty.call(obj, key)) {
326-
;(clonedObj as any)[key] = partialClone(obj[key], depth - 1, omitKeys, replacement)
337+
;(clonedObj as any)[key] = partialClone(obj[key], depth - 1, omitKeys, options)
327338
}
328339
}
329340

packages/core/src/shared/utilities/textUtilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { default as stripAnsi } from 'strip-ansi'
1010
import { getLogger } from '../logger/logger'
1111

1212
/**
13-
* Truncates string `s` if it exceeds `n` chars.
13+
* Truncates string `s` if it has or exceeds `n` chars.
1414
*
1515
* If `n` is negative, truncates at start instead of end.
1616
*

packages/core/src/shared/vscode/commands2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ async function runCommand<T extends Callback>(fn: T, info: CommandInfo<T>): Prom
653653

654654
logger.debug(
655655
`command: running ${label} with arguments: %O`,
656-
partialClone(args, 3, ['clientSecret', 'accessToken', 'refreshToken', 'tooltip'], '[omitted]')
656+
partialClone(args, 3, ['clientSecret', 'accessToken', 'refreshToken', 'tooltip'], { replacement: '[omitted]' })
657657
)
658658

659659
try {

packages/core/src/test/shared/utilities/collectionUtils.test.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -730,8 +730,8 @@ describe('CollectionUtils', async function () {
730730
d: {},
731731
e: {},
732732
})
733-
assert.deepStrictEqual(partialClone(testObj, 0, [], '[replaced]'), '[replaced]')
734-
assert.deepStrictEqual(partialClone(testObj, 1, [], '[replaced]'), {
733+
assert.deepStrictEqual(partialClone(testObj, 0, [], { replacement: '[omitted]' }), '[replaced]')
734+
assert.deepStrictEqual(partialClone(testObj, 1, [], { replacement: '[omitted]' }), {
735735
...testObj,
736736
d: '[replaced]',
737737
e: '[replaced]',
@@ -758,7 +758,7 @@ describe('CollectionUtils', async function () {
758758
},
759759
}
760760

761-
assert.deepStrictEqual(partialClone(testObj, 2, ['c', 'e2'], '[omitted]'), {
761+
assert.deepStrictEqual(partialClone(testObj, 2, ['c', 'e2'], { replacement: '[omitted]' }), {
762762
...testObj,
763763
c: '[omitted]',
764764
d: { d1: '[omitted]' },
@@ -767,7 +767,7 @@ describe('CollectionUtils', async function () {
767767
e2: '[omitted]',
768768
},
769769
})
770-
assert.deepStrictEqual(partialClone(testObj, 3, ['c', 'e2'], '[omitted]'), {
770+
assert.deepStrictEqual(partialClone(testObj, 3, ['c', 'e2'], { replacement: '[omitted]' }), {
771771
...testObj,
772772
c: '[omitted]',
773773
d: { d1: { d2: '[omitted]' } },
@@ -777,5 +777,30 @@ describe('CollectionUtils', async function () {
777777
},
778778
})
779779
})
780+
781+
it('truncates properties by maxLength', function () {
782+
const testObj = {
783+
a: '1',
784+
b: '11',
785+
c: '11111',
786+
d: {
787+
e: {
788+
a: '11111',
789+
b: '11',
790+
},
791+
},
792+
}
793+
assert.deepStrictEqual(partialClone(testObj, 5, [], { maxLength: 2 }), {
794+
a: '1',
795+
b: '11',
796+
c: '11...',
797+
d: {
798+
e: {
799+
a: '11...',
800+
b: '11',
801+
},
802+
},
803+
})
804+
})
780805
})
781806
})

0 commit comments

Comments
 (0)