|
1 | 1 | import { constructPayload } from '../../utils/utils'; |
2 | | -import { removeUndefinedAndNullAndEmptyValues } from '../../utils/commonUtils'; |
| 2 | +import { |
| 3 | + isDefinedAndNotNullAndNotEmpty, |
| 4 | + removeUndefinedAndNullAndEmptyValues, |
| 5 | +} from '../../utils/commonUtils'; |
3 | 6 |
|
4 | 7 | /** |
5 | 8 | * Braze recommended ecommerce event names. |
@@ -131,14 +134,13 @@ const PER_EVENT_MAPPING = { |
131 | 134 | // --------------------------------------------------------------------------- |
132 | 135 |
|
133 | 136 | /** |
134 | | - * Mirror `constructPayload`'s truthiness rule: `0` and `false` are valid values; only |
135 | | - * undefined/null/empty-string count as missing. |
| 137 | + * A field counts as "resolved" iff it survives the outgoing payload scrub |
| 138 | + * (`removeUndefinedAndNullAndEmptyValues`). Reuse that exact predicate so the |
| 139 | + * missing-required-field warning can never drift from what's actually sent — e.g. a |
| 140 | + * required field of `{}`/`[]`/`''` is both stripped from the payload AND warned, while |
| 141 | + * `0`/`false`/numbers stay valid. |
136 | 142 | */ |
137 | | -const isResolvedValue = value => { |
138 | | - if (value === 0 || value === false) return true; |
139 | | - if (value === undefined || value === null) return false; |
140 | | - return !(typeof value === 'string' && value.length === 0); |
141 | | -}; |
| 143 | +const isResolvedValue = isDefinedAndNotNullAndNotEmpty; |
142 | 144 |
|
143 | 145 | /** |
144 | 146 | * Return the subset of `source` whose keys are not in `consumed`, with undefined/null/empty |
@@ -201,10 +203,7 @@ const consumedTopLevelKeysForEvent = (brazeEvent, eventMapping, hasProducts, pro |
201 | 203 | // explicit `products[]` is provided; in that case mark those keys as consumed so they |
202 | 204 | // don't duplicate into event-level metadata. When `products[]` is present, the top-level |
203 | 205 | // fields are untouched and must flow through to metadata. |
204 | | - if ( |
205 | | - brazeEvent === BRAZE_ECOMMERCE_EVENTS.CART_UPDATED && |
206 | | - !Array.isArray(properties.products) |
207 | | - ) { |
| 206 | + if (brazeEvent === BRAZE_ECOMMERCE_EVENTS.CART_UPDATED && !Array.isArray(properties.products)) { |
208 | 207 | consumedKeysFromMapping(ECOMMERCE_PRODUCT_MAPPING).forEach(key => consumed.add(key)); |
209 | 208 | } |
210 | 209 |
|
@@ -300,11 +299,15 @@ export const getEcommerceMapping = eventName => { |
300 | 299 | /** |
301 | 300 | * Derive the Braze `source` field. On the web SDK this resolves to `'web'`, unless the |
302 | 301 | * event carries an explicit, valid `properties.source` (`web`/`ios`/`android`), which |
303 | | - * takes precedence. Always returns one of Braze's enum values; never undefined. |
| 302 | + * takes precedence. The explicit value is trimmed and lowercased before matching, so |
| 303 | + * surrounding whitespace doesn't defeat it. Always returns one of Braze's enum values; |
| 304 | + * never undefined. |
304 | 305 | */ |
305 | 306 | export const deriveSource = message => { |
306 | 307 | const properties = message.properties || {}; |
307 | | - const explicit = String(properties.source || '').toLowerCase(); |
| 308 | + const explicit = String(properties.source || '') |
| 309 | + .trim() |
| 310 | + .toLowerCase(); |
308 | 311 | if (BRAZE_SOURCE_VALUES.indexOf(explicit) !== -1) { |
309 | 312 | return explicit; |
310 | 313 | } |
|
0 commit comments