Skip to content

Commit 9e3fd99

Browse files
committed
strengthen types, implement entire class
1 parent e4abcca commit 9e3fd99

File tree

5 files changed

+221
-37
lines changed

5 files changed

+221
-37
lines changed

package-lock.json

Lines changed: 43 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242
"@segment/analytics-react-native": "*"
4343
},
4444
"dependencies": {
45-
"@fullstory/react-native": "^1.1.1"
45+
"@fullstory/react-native": "^1.1.1",
46+
"lodash.isplainobject": "^4.0.6"
4647
},
4748
"devDependencies": {
4849
"@types/jest": "^28.1.2",
50+
"@types/lodash.isplainobject": "^4.0.7",
4951
"prettier": "^2.8.1",
5052
"react-native-builder-bob": "^0.20.3",
5153
"rimraf": "^3.0.2",
Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1+
import {
2+
eCommerceEventsProductListFilteredProcessed,
3+
eCommerceEventsProductListFilteredRaw,
4+
} from './__fixtures__/FSSampleEvents';
15
import { FSSuffixedProperties } from '../utils/FSSuffixedProperties';
26

37
describe('FSSuffixedProperties', () => {
4-
const fsSuffixedProperties = new FSSuffixedProperties();
8+
const fsSuffixedProperties = new FSSuffixedProperties({});
59

610
describe('Test getSuffixStringFromSimpleObject()', () => {
7-
test('Should return correct Date suffix', () => {
8-
const validDate = new Date();
9-
expect(
10-
fsSuffixedProperties.getSuffixStringFromSimpleObject(validDate)
11-
).toBe('_date');
12-
});
11+
// TODO: JsonValue does not include dates
12+
// test('Should return correct Date suffix', () => {
13+
// const validDate = new Date();
14+
// expect(
15+
// fsSuffixedProperties.getSuffixStringFromSimpleObject(
16+
// validDate.toString()
17+
// )
18+
// ).toBe('_date');
19+
// });
1320
test('Should return correct String suffix', () => {
1421
expect(
1522
fsSuffixedProperties.getSuffixStringFromSimpleObject('sample string')
@@ -37,15 +44,17 @@ describe('FSSuffixedProperties', () => {
3744

3845
describe('Test addSimpleObject()', () => {
3946
beforeEach(() => {
40-
fsSuffixedProperties.suffixedProperties = {};
47+
fsSuffixedProperties.initialize({});
4148
});
4249

4350
test('Should add simple string object', () => {
4451
const key = 'input.key_str';
4552
const val = 'val';
4653
fsSuffixedProperties.addSimpleObject(key, val);
4754

48-
expect(fsSuffixedProperties.suffixedProperties).toEqual({ [key]: val });
55+
expect(fsSuffixedProperties.getSuffixedProperties()).toEqual({
56+
[key]: val,
57+
});
4958
});
5059

5160
test('Should add duplicate simple string object', () => {
@@ -57,39 +66,61 @@ describe('FSSuffixedProperties', () => {
5766
fsSuffixedProperties.addSimpleObject(key, val2);
5867
fsSuffixedProperties.addSimpleObject(key, val3);
5968

60-
expect(fsSuffixedProperties.suffixedProperties).toEqual({
69+
expect(fsSuffixedProperties.getSuffixedProperties()).toEqual({
6170
[key]: [val, val2, val3],
6271
});
6372
});
6473
});
6574

66-
describe('Test pluralizeAllArrayKeys()', () => {
75+
describe('Test initialize()', () => {
6776
beforeEach(() => {
68-
fsSuffixedProperties.suffixedProperties = {};
77+
fsSuffixedProperties.initialize({});
6978
});
7079

71-
test('Should not pluralize', () => {
72-
const key = 'input.key_str';
73-
const val = 'val';
74-
fsSuffixedProperties.addSimpleObject(key, val);
75-
fsSuffixedProperties.pluralizeAllArrayKeys();
80+
test('Should flatten map', () => {
81+
const data = {
82+
input: [
83+
{ key1: 'val1', key2: 'val2' },
84+
{ key1: 'secondVal1', key2: 'secondVal2' },
85+
],
86+
};
87+
88+
fsSuffixedProperties.initialize(data);
7689

77-
expect(fsSuffixedProperties.suffixedProperties).toEqual({ [key]: val });
90+
expect(fsSuffixedProperties.getSuffixedProperties()).toEqual({
91+
['input.key1_strs']: ['val1', 'secondVal1'],
92+
['input.key2_strs']: ['val2', 'secondVal2'],
93+
});
7894
});
7995

80-
test('Should pluralize', () => {
81-
const key = 'input.key_str';
82-
const val = 'val';
83-
const val2 = 'val2';
84-
const val3 = 'val3';
85-
fsSuffixedProperties.addSimpleObject(key, val);
86-
fsSuffixedProperties.addSimpleObject(key, val2);
87-
fsSuffixedProperties.addSimpleObject(key, val3);
88-
fsSuffixedProperties.pluralizeAllArrayKeys();
96+
test('Should return correct suffixed keys', () => {
97+
// TODO: JsonValue does not include dates
98+
// const date = new Date();
99+
const data = {
100+
int: 3,
101+
float: 3.14,
102+
bool: true,
103+
// date: date,
104+
strings: ['hello', 'goodbye'],
105+
};
89106

90-
expect(fsSuffixedProperties.suffixedProperties).toEqual({
91-
['input.key_strs']: [val, val2, val3],
107+
fsSuffixedProperties.initialize(data);
108+
109+
expect(fsSuffixedProperties.getSuffixedProperties()).toEqual({
110+
['int_int']: 3,
111+
['float_real']: 3.14,
112+
['bool_bool']: true,
113+
// ['date_date']: date,
114+
['strings_strs']: ['hello', 'goodbye'],
92115
});
93116
});
117+
118+
test('Should return correct EcommerceEvent', () => {
119+
fsSuffixedProperties.initialize(eCommerceEventsProductListFilteredRaw);
120+
121+
expect(fsSuffixedProperties.getSuffixedProperties()).toEqual(
122+
eCommerceEventsProductListFilteredProcessed
123+
);
124+
});
94125
});
95126
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
export const eCommerceEventsProductListFilteredRaw = {
2+
list_id: 'todays_deals_may_11_2019',
3+
filters: [
4+
{
5+
type: 'department',
6+
value: 'beauty',
7+
},
8+
{
9+
type: 'price',
10+
value: 'under-$25',
11+
},
12+
],
13+
sorts: [
14+
{
15+
type: 'price',
16+
value: 'desc',
17+
},
18+
],
19+
products: [
20+
{
21+
product_id: '507f1f77bcf86cd798439011',
22+
sku: '45360-32',
23+
name: 'Special Facial Soap',
24+
price: 12.6,
25+
position: 1,
26+
category: 'Beauty',
27+
url: 'https://www.example.com/product/path',
28+
image_url: 'https://www.example.com/product/path.jpg',
29+
},
30+
{
31+
product_id: '505bd76785ebb509fc283733',
32+
sku: '46573-32',
33+
name: 'Fancy Hairbrush',
34+
price: 7.6,
35+
position: 2,
36+
category: 'Beauty',
37+
},
38+
],
39+
};
40+
41+
export const eCommerceEventsProductListFilteredProcessed = {
42+
'list_id_str': 'todays_deals_may_11_2019',
43+
'filters.type_strs': ['department', 'price'],
44+
'filters.value_strs': ['beauty', 'under-$25'],
45+
'sorts.type_str': 'price',
46+
'sorts.value_str': 'desc',
47+
'products.product_id_strs': [
48+
'507f1f77bcf86cd798439011',
49+
'505bd76785ebb509fc283733',
50+
],
51+
'products.sku_strs': ['45360-32', '46573-32'],
52+
'products.name_strs': ['Special Facial Soap', 'Fancy Hairbrush'],
53+
'products.price_reals': [12.6, 7.6],
54+
'products.position_ints': [1, 2],
55+
'products.category_strs': ['Beauty', 'Beauty'],
56+
'products.url_str': 'https://www.example.com/product/path',
57+
'products.image_url_str': 'https://www.example.com/product/path.jpg',
58+
};

src/utils/FSSuffixedProperties.ts

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,74 @@
1+
import isPlainObject from 'lodash.isplainobject';
2+
3+
import type {
4+
JsonList,
5+
JsonMap,
6+
JsonValue,
7+
} from '@segment/analytics-react-native';
8+
19
export class FSSuffixedProperties {
2-
suffixedProperties: { [key: string]: any } = {};
10+
private suffixedProperties: { [key: string]: any } = {};
11+
12+
constructor(properties: JsonMap) {
13+
this.initialize(properties);
14+
}
15+
16+
initialize(properties: JsonMap) {
17+
this.suffixedProperties = {};
18+
19+
const stack = [properties];
20+
21+
while (!!stack.length) {
22+
const nextElement = stack.shift();
23+
24+
if (nextElement === null || nextElement === undefined) continue;
25+
26+
for (const key in nextElement) {
27+
if (isPlainObject(nextElement[key])) {
28+
const innerObject = nextElement[key] as JsonMap;
329

4-
getSuffixStringFromSimpleObject(item: any): String {
30+
for (const k in innerObject as JsonMap) {
31+
const concatenatedKey = key + '.' + k;
32+
33+
stack.push({ [concatenatedKey]: innerObject[k] });
34+
}
35+
} else if (Array.isArray(nextElement[key])) {
36+
for (const item of nextElement[key] as JsonList) {
37+
stack.push({ [key]: item });
38+
}
39+
} else {
40+
const suffix = this.getSuffixStringFromSimpleObject(nextElement[key]);
41+
const newSuffix = key + suffix;
42+
this.addSimpleObject(newSuffix, nextElement[key]);
43+
}
44+
}
45+
}
46+
47+
this.pluralizeAllArrayKeys();
48+
}
49+
50+
getSuffixStringFromSimpleObject(item: JsonValue): String {
551
let suffix = '';
652

7-
if (!isNaN(item) && !isNaN(parseFloat(item))) {
53+
if (!isNaN(Number(item)) && !isNaN(parseFloat(String(item)))) {
854
if (Number.isInteger(item)) {
955
suffix = '_int';
1056
} else {
1157
suffix = '_real';
1258
}
1359
} else if (item === true || item === false) {
1460
suffix = '_bool';
15-
} else if (Object.prototype.toString.call(item) === '[object Date]') {
16-
suffix = '_date';
61+
// TODO: JsonValue does not include dates
62+
// } else if (Object.prototype.toString.call(item) === '[object Date]') {
63+
// suffix = '_date';
1764
} else if (typeof item === 'string' || item instanceof String) {
1865
suffix = '_str';
1966
}
2067

2168
return suffix;
2269
}
2370

24-
addSimpleObject(key: string, obj: Object) {
71+
addSimpleObject(key: string, obj: JsonValue) {
2572
if (!!this.suffixedProperties[key]) {
2673
if (Array.isArray(this.suffixedProperties[key])) {
2774
this.suffixedProperties[key].push(obj);
@@ -41,4 +88,8 @@ export class FSSuffixedProperties {
4188
}
4289
}
4390
}
91+
92+
getSuffixedProperties() {
93+
return this.suffixedProperties;
94+
}
4495
}

0 commit comments

Comments
 (0)