@@ -8,10 +8,12 @@ import validate from './validate'
8
8
import { arrify } from '../arrify'
9
9
import { flattenObject } from './flatten'
10
10
import { evaluateLiquid } from './liquid-directive'
11
+ import { StatsContext } from '../destination-kit'
12
+ import { isLiquidDirective } from './value-keys'
11
13
12
14
export type InputData = { [ key : string ] : unknown }
13
15
export type Features = { [ key : string ] : boolean }
14
- type Directive = ( options : JSONValue , payload : JSONObject ) => JSONLike
16
+ type Directive = ( options : JSONValue , payload : JSONObject , statsContext ?: StatsContext | undefined ) => JSONLike
15
17
type StringDirective = ( value : string , payload : JSONObject ) => JSONLike
16
18
17
19
interface Directives {
@@ -42,7 +44,7 @@ function registerStringDirective(name: string, fn: StringDirective): void {
42
44
} )
43
45
}
44
46
45
- function runDirective ( obj : JSONObject , payload : JSONObject ) : JSONLike {
47
+ function runDirective ( obj : JSONObject , payload : JSONObject , statsContext ?: StatsContext | undefined ) : JSONLike {
46
48
const name = Object . keys ( obj ) . find ( ( key ) => key . startsWith ( '@' ) ) as string
47
49
const directiveFn = directives [ name ]
48
50
const value = obj [ name ]
@@ -51,6 +53,10 @@ function runDirective(obj: JSONObject, payload: JSONObject): JSONLike {
51
53
throw new Error ( `${ name } is not a valid directive, got ${ realTypeOf ( directiveFn ) } ` )
52
54
}
53
55
56
+ if ( name === '@liquid' ) {
57
+ return directiveFn ( value , payload , statsContext )
58
+ }
59
+
54
60
return directiveFn ( value , payload )
55
61
}
56
62
@@ -326,8 +332,8 @@ registerDirective('@excludeWhenNull', (value, payload) => {
326
332
return cleanNulls ( resolved )
327
333
} )
328
334
329
- registerDirective ( '@liquid' , ( opts , payload ) => {
330
- return evaluateLiquid ( opts , payload )
335
+ registerDirective ( '@liquid' , ( opts , payload , statsContext ) => {
336
+ return evaluateLiquid ( opts , payload , statsContext )
331
337
} )
332
338
333
339
// Recursively remove all null values from an object
@@ -381,23 +387,40 @@ function getMappingToProcess(mapping: JSONLikeObject): JSONLikeObject {
381
387
* @param payload - the input data to apply to the mapping directives
382
388
* @todo support arrays or array directives?
383
389
*/
384
- function resolve ( mapping : JSONLike , payload : JSONObject ) : JSONLike {
390
+ function resolve ( mapping : JSONLike , payload : JSONObject , statsContext ?: StatsContext | undefined ) : JSONLike {
385
391
if ( ! isObject ( mapping ) && ! isArray ( mapping ) ) {
386
392
return mapping
387
393
}
388
394
389
395
if ( isDirective ( mapping ) ) {
396
+ if ( isLiquidDirective ( mapping ) ) {
397
+ // Only include stats, and therefore extra fieldKey tags, if the mapping is a liquid directive to save on costs
398
+ return runDirective ( mapping , payload , statsContext )
399
+ }
400
+
390
401
return runDirective ( mapping , payload )
391
402
}
392
403
393
404
if ( Array . isArray ( mapping ) ) {
394
- return mapping . map ( ( value ) => resolve ( value , payload ) )
405
+ return mapping . map ( ( value ) => resolve ( value , payload , statsContext ) )
395
406
}
396
407
397
408
const resolved : JSONLikeObject = { }
398
409
399
410
for ( const key of Object . keys ( mapping ) ) {
400
- resolved [ key ] = resolve ( mapping [ key ] , payload )
411
+ let originalTags : string [ ] = [ ]
412
+ const statsTagsExist = statsContext ?. tags !== undefined
413
+
414
+ if ( statsTagsExist ) {
415
+ originalTags = statsContext . tags
416
+ statsContext . tags = [ ...statsContext . tags , `fieldKey:${ key } ` ]
417
+ }
418
+
419
+ resolved [ key ] = resolve ( mapping [ key ] , payload , statsContext )
420
+
421
+ if ( statsTagsExist ) {
422
+ statsContext . tags = originalTags
423
+ }
401
424
}
402
425
403
426
return resolved
@@ -409,7 +432,11 @@ function resolve(mapping: JSONLike, payload: JSONObject): JSONLike {
409
432
* @param mapping - the directives and raw values
410
433
* @param data - the input data to apply to directives
411
434
*/
412
- function transform ( mapping : JSONLikeObject , data : InputData | undefined = { } ) : JSONObject {
435
+ function transform (
436
+ mapping : JSONLikeObject ,
437
+ data : InputData | undefined = { } ,
438
+ statsContext ?: StatsContext | undefined
439
+ ) : JSONObject {
413
440
const realType = realTypeOf ( data )
414
441
if ( realType !== 'object' ) {
415
442
throw new Error ( `data must be an object, got ${ realType } ` )
@@ -420,7 +447,7 @@ function transform(mapping: JSONLikeObject, data: InputData | undefined = {}): J
420
447
// throws if the mapping config is invalid
421
448
validate ( mappingToProcess )
422
449
423
- const resolved = resolve ( mappingToProcess , data as JSONObject )
450
+ const resolved = resolve ( mappingToProcess , data as JSONObject , statsContext )
424
451
const cleaned = removeUndefined ( resolved )
425
452
426
453
// Cast because we know there are no `undefined` values anymore
@@ -432,7 +459,11 @@ function transform(mapping: JSONLikeObject, data: InputData | undefined = {}): J
432
459
* @param mapping - the directives and raw values
433
460
* @param data - the array input data to apply to directives
434
461
*/
435
- function transformBatch ( mapping : JSONLikeObject , data : Array < InputData > | undefined = [ ] ) : JSONObject [ ] {
462
+ function transformBatch (
463
+ mapping : JSONLikeObject ,
464
+ data : Array < InputData > | undefined = [ ] ,
465
+ statsContext ?: StatsContext | undefined
466
+ ) : JSONObject [ ] {
436
467
const realType = realTypeOf ( data )
437
468
if ( ! isArray ( data ) ) {
438
469
throw new Error ( `data must be an array, got ${ realType } ` )
@@ -443,7 +474,7 @@ function transformBatch(mapping: JSONLikeObject, data: Array<InputData> | undefi
443
474
// throws if the mapping config is invalid
444
475
validate ( mappingToProcess )
445
476
446
- const resolved = data . map ( ( d ) => resolve ( mappingToProcess , d as JSONObject ) )
477
+ const resolved = data . map ( ( d ) => resolve ( mappingToProcess , d as JSONObject , statsContext ) )
447
478
448
479
// Cast because we know there are no `undefined` values after `removeUndefined`
449
480
return removeUndefined ( resolved ) as JSONObject [ ]
0 commit comments