Skip to content

Commit 193cc88

Browse files
Property value support
1 parent 30360da commit 193cc88

File tree

4 files changed

+72
-32
lines changed

4 files changed

+72
-32
lines changed

packages/types/src/mapping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export interface ValueConfig {
4444
}
4545

4646
export type Condition = (
47-
obj: WalkerOS.AnyObject,
47+
value: WalkerOS.DeepPartialEvent | unknown,
4848
mapping?: Value,
4949
instance?: WalkerOS.Instance,
5050
) => boolean;
@@ -53,7 +53,7 @@ export type Fn = (
5353
event: WalkerOS.PartialEvent,
5454
mapping: Value,
5555
options: Options,
56-
) => WalkerOS.Property | void;
56+
) => WalkerOS.Property | unknown;
5757

5858
export type Loop = [Value, Value];
5959

packages/types/src/walkeros.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Destination, Elb, Hooks } from '.';
22

3-
export type AnyObject = Record<string, unknown>;
3+
export type AnyObject<T = unknown> = Record<string, T>;
44
export type AnyFunction = (...args: unknown[]) => unknown;
55
export type SingleOrArray<T> = T | Array<T>;
66

packages/utils/src/__tests__/mapping.test.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getEvent,
55
getMappingEvent,
66
getMappingValue,
7+
isObject,
78
isString,
89
} from '../core';
910

@@ -82,8 +83,10 @@ describe('getMappingEvent', () => {
8283
order: {
8384
complete: [
8485
{
85-
condition: (event: WalkerOS.PartialEvent) =>
86-
event.globals?.env === 'prod',
86+
condition: (event) =>
87+
isObject(event) &&
88+
isObject(event.globals) &&
89+
event.globals.env === 'prod',
8790
ignore: true,
8891
},
8992
{
@@ -153,17 +156,21 @@ describe('getMappingValue', () => {
153156

154157
test('key default', () => {
155158
const event = createEvent();
159+
156160
expect(
157161
getMappingValue(event, { key: 'data.string', value: 'static' }),
158162
).toBe(event.data.string);
163+
159164
expect(
160165
getMappingValue(event, { key: 'does.not.exist', value: 'fallback' }),
161166
).toBe('fallback');
167+
162168
expect(getMappingValue(event, { value: 'static' })).toBe('static');
163169
});
164170

165171
test('empty', () => {
166-
expect(getMappingValue(createEvent(), {})).toBeUndefined();
172+
const event = createEvent();
173+
expect(getMappingValue(event)).toStrictEqual(event);
167174
});
168175

169176
test('false', () => {
@@ -211,7 +218,8 @@ describe('getMappingValue', () => {
211218
loop: [
212219
'nested',
213220
{
214-
condition: (entity) => entity.type === 'product',
221+
condition: (entity) =>
222+
isObject(entity) && entity.type === 'product',
215223
key: 'data.name',
216224
},
217225
],
@@ -292,6 +300,27 @@ describe('getMappingValue', () => {
292300
).toBe('fallback');
293301
});
294302

303+
test('values', () => {
304+
expect(
305+
getMappingValue(
306+
{ arr: [1, 'foo', false] },
307+
{
308+
loop: [
309+
'arr',
310+
{
311+
fn: (i: unknown) => {
312+
return i;
313+
},
314+
},
315+
],
316+
},
317+
),
318+
).toStrictEqual([1, 'foo', false]);
319+
320+
expect(getMappingValue('string')).toEqual('string');
321+
expect(getMappingValue(1)).toEqual(1);
322+
});
323+
295324
test('consent', () => {
296325
const event = createEvent({ consent: { functional: true } });
297326
const instance = {

packages/utils/src/core/mapping.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Mapping, WalkerOS } from '@elbwalker/types';
22
import { getGrantedConsent } from './consent';
33
import { getByPath } from './byPath';
4-
import { isArray, isDefined, isObject, isString } from './is';
4+
import { isArray, isDefined, isString } from './is';
55
import { castToProperty } from './property';
66

77
export function getMappingEvent(
@@ -51,20 +51,22 @@ export function getMappingEvent(
5151
}
5252

5353
export function getMappingValue(
54-
obj: WalkerOS.PartialEvent | WalkerOS.AnyObject,
55-
data: Mapping.Data,
54+
value: WalkerOS.DeepPartialEvent | unknown | undefined,
55+
data: Mapping.Data = {},
5656
options: Mapping.Options = {},
5757
): WalkerOS.Property | undefined {
58+
if (!isDefined(value)) return;
59+
5860
const mappings = isArray(data) ? data : [data];
5961

6062
for (const mapping of mappings) {
61-
const result = processMappingValue(obj, mapping, options);
63+
const result = processMappingValue(value, mapping, options);
6264
if (isDefined(result)) return result;
6365
}
6466
}
6567

6668
function processMappingValue(
67-
obj: WalkerOS.PartialEvent | WalkerOS.AnyObject,
69+
value: WalkerOS.DeepPartialEvent | unknown,
6870
mapping: Mapping.Value,
6971
options: Mapping.Options = {},
7072
): WalkerOS.Property | undefined {
@@ -77,61 +79,70 @@ function processMappingValue(
7779
return mappings.reduce((acc, mappingItem) => {
7880
if (acc) return acc; // A valid result was already found
7981

80-
const { condition, consent, fn, key, loop, map, set, validate, value } =
81-
isString(mappingItem)
82-
? ({ key: mappingItem } as Mapping.ValueConfig)
83-
: mappingItem;
82+
const {
83+
condition,
84+
consent,
85+
fn,
86+
key,
87+
loop,
88+
map,
89+
set,
90+
validate,
91+
value: staticValue,
92+
} = isString(mappingItem) ? { key: mappingItem } : mappingItem;
8493

8594
// Check if this mapping should be used
86-
if (condition && !condition(obj, mappingItem, instance)) return;
95+
if (condition && !condition(value, mappingItem, instance)) return;
8796

8897
// Check if consent is required and granted
89-
if (consent && !getGrantedConsent(consent, instance?.consent)) return value;
98+
if (consent && !getGrantedConsent(consent, instance?.consent))
99+
return staticValue;
100+
101+
let mappingValue: unknown = staticValue || value;
90102

91-
let mappingValue;
92103
if (fn) {
93104
// Use a custom function to get the value
94-
mappingValue = fn(obj, mappingItem, options);
95-
} else {
105+
mappingValue = fn(value as WalkerOS.PartialEvent, mappingItem, options); // @TODO stop casting
106+
}
107+
108+
if (key) {
96109
// Get dynamic value from the event
97-
mappingValue = getByPath(obj, key, value);
110+
mappingValue = getByPath(value, key, staticValue);
98111
}
99112

100113
if (loop) {
101114
const [scope, itemMapping] = loop;
102115

103116
const data =
104-
scope === 'this' ? [obj] : getMappingValue(obj, scope, options);
117+
scope === 'this' ? [value] : getMappingValue(value, scope, options);
105118

106119
if (isArray(data)) {
107120
mappingValue = data
108-
.map((item) =>
109-
getMappingValue(isObject(item) ? item : {}, itemMapping, options),
110-
)
121+
.map((item) => getMappingValue(item, itemMapping, options))
111122
.filter(isDefined);
112123
}
113124
} else if (map) {
114125
mappingValue = Object.entries(map).reduce(
115126
(mappedObj, [mapKey, mapValue]) => {
116-
const result = getMappingValue(obj, mapValue, options);
127+
const result = getMappingValue(value, mapValue, options);
117128
if (isDefined(result)) mappedObj[mapKey] = result;
118129

119130
return mappedObj;
120131
},
121132
{} as WalkerOS.AnyObject,
122133
);
123134
} else if (set) {
124-
mappingValue = set.map((item) => processMappingValue(obj, item, options));
135+
mappingValue = set.map((item) =>
136+
processMappingValue(value, item, options),
137+
);
125138
}
126139

127140
// Validate the value
128-
if (validate && !validate(mappingValue)) {
129-
mappingValue = undefined;
130-
}
141+
if (validate && !validate(mappingValue)) mappingValue = undefined;
131142

132143
const property = castToProperty(mappingValue);
133144

134145
// Finally, check and convert the type
135-
return isDefined(property) ? property : value; // Always use value as a fallback
146+
return isDefined(property) ? property : castToProperty(staticValue); // Always use value as a fallback
136147
}, undefined as WalkerOS.Property | undefined);
137148
}

0 commit comments

Comments
 (0)