Skip to content

Commit e2ca5e7

Browse files
authored
Package update and IConvertOption (#17)
* update packages * IConvertOption now happens before data is assigned to properties. Now if you use ConversionTypeEnum.JSON and have a default model it will create that new model instead of having a plain object.
1 parent cf17bf0 commit e2ca5e7

10 files changed

Lines changed: 1270 additions & 815 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ export default class CarModel extends BaseModel {
318318

319319
## Release History
320320

321+
- 2019-06-23 v1.9.0 IConvertOption now happens before data is assigned to properties. Now if you use ConversionTypeEnum.JSON and have a default model it will create that new model instead of having a plain object.
322+
321323
- 2019-01-28 v1.8.2 ConversionTypeEnum.String now coverts objects with JSON.stringify.
322324

323325
- 2019-01-28 v1.8.1 Returns the original data for ConversionTypeEnum.JSON if parse fails.

__tests__/BaseModel.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {UserResponseModel} from './data/models/UserResponseModel';
66
import {ConversionInfoModel} from './data/models/ConversionInfoModel';
77
import {NonExistentKeyConversionModel} from './data/models/NonExistentKeyConversionModel';
88
import {NameModel} from './data/models/NameModel';
9+
import {AdminModel} from './data/models/AdminModel';
910

1011
describe('BaseModel', () => {
1112
let json: any = null;
@@ -26,7 +27,24 @@ describe('BaseModel', () => {
2627
test('should populate InfoModel', () => {
2728
const model = new InfoModel(json.info);
2829

29-
expect(model.toJSON()).toEqual(json.info);
30+
expect(model.toJSON()).toEqual({
31+
...json.info,
32+
stringified: JSON.parse(json.info.stringified),
33+
});
34+
expect(model.stringified).toBeInstanceOf(AdminModel);
35+
});
36+
37+
test('should have default of InfoModel', () => {
38+
const model = new InfoModel();
39+
40+
expect(model.toJSON()).toEqual({
41+
page: null,
42+
results: null,
43+
seed: '',
44+
stringified: null,
45+
version: '',
46+
});
47+
expect(model.stringified).toEqual(null);
3048
});
3149

3250
test('should have default of UserResponseModel', () => {
@@ -86,19 +104,26 @@ describe('BaseModel', () => {
86104
results: json.results.map((resultItem: any) =>
87105
Util.deletePropertyFromObject(resultItem, ['location', 'login', 'dob', 'registered', 'phone', 'cell', 'id'])
88106
),
107+
info: {
108+
...json.info,
109+
stringified: JSON.parse(json.info.stringified),
110+
},
89111
};
90112

91113
expect(model.toJSON()).toEqual(expectedData);
92114
});
93115

94116
test('should call update with empty object and have no changes', () => {
95117
const model = new UserResponseModel(json);
96-
97118
const expectedData = {
98119
...json,
99120
results: json.results.map((resultItem: any) =>
100121
Util.deletePropertyFromObject(resultItem, ['location', 'login', 'dob', 'registered', 'phone', 'cell', 'id'])
101122
),
123+
info: {
124+
...json.info,
125+
stringified: JSON.parse(json.info.stringified),
126+
},
102127
};
103128

104129
model.update({});
@@ -108,13 +133,18 @@ describe('BaseModel', () => {
108133

109134
test('should have populate UserResponseModel from same UserResponseModel', () => {
110135
let model = new UserResponseModel(json);
136+
111137
model = new UserResponseModel(model);
112138

113139
const expectedData = {
114140
...json,
115141
results: json.results.map((resultItem: any) =>
116142
Util.deletePropertyFromObject(resultItem, ['location', 'login', 'dob', 'registered', 'phone', 'cell', 'id'])
117143
),
144+
info: {
145+
...json.info,
146+
stringified: JSON.parse(json.info.stringified),
147+
},
118148
};
119149

120150
expect(model.toJSON()).toEqual(expectedData);
@@ -153,6 +183,10 @@ describe('BaseModel', () => {
153183
},
154184
},
155185
],
186+
info: {
187+
...json.info,
188+
stringified: JSON.parse(json.info.stringified),
189+
},
156190
});
157191
});
158192

@@ -193,6 +227,12 @@ describe('BaseModel', () => {
193227
results: null,
194228
page: null,
195229
version: '',
230+
stringified: {
231+
description: '',
232+
id: '',
233+
name: '',
234+
userCount: 0,
235+
},
196236
},
197237
results: [],
198238
resultsAny: [],

__tests__/Util.test.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -105,39 +105,7 @@ describe('Util', () => {
105105
expect(Util.convertDataToConversionType(json, ConversionTypeEnum.JSON)).toEqual(json); // should return same
106106
});
107107

108-
describe('Util.convertDataUsingConversionOptions()', () => {
109-
test('Should convert data types', () => {
110-
const data: object = {
111-
stringToFloat: '23.345',
112-
stringToNumber: '23.345',
113-
stringToFalse: '0',
114-
stringToTrue: '1',
115-
noChange: '8',
116-
numberToString: 8,
117-
stringToJson: '{"complete":"Complete","new":"New","open":"Open"}',
118-
};
119-
const conversionOptions: IConversionOption = {
120-
stringToFloat: ConversionTypeEnum.Float,
121-
stringToNumber: ConversionTypeEnum.Number,
122-
stringToFalse: ConversionTypeEnum.Boolean,
123-
stringToTrue: ConversionTypeEnum.Boolean,
124-
numberToString: ConversionTypeEnum.String,
125-
stringToJson: ConversionTypeEnum.JSON,
126-
};
127-
128-
Util.convertDataUsingConversionOptions(data, conversionOptions);
129-
130-
expect(data).toEqual({
131-
stringToFloat: 23.345,
132-
stringToNumber: 23,
133-
stringToFalse: false,
134-
stringToTrue: true,
135-
noChange: '8',
136-
numberToString: '8',
137-
stringToJson: {complete: 'Complete', new: 'New', open: 'Open'},
138-
});
139-
});
140-
108+
describe('Util.validConversionOptionNames()', () => {
141109
test('should throw error on non existent key', () => {
142110
const data: any = {
143111
something: '',
@@ -147,7 +115,7 @@ describe('Util', () => {
147115
};
148116

149117
expect(() => {
150-
Util.convertDataUsingConversionOptions(data, conversionOptions);
118+
Util.validConversionOptionNames(data, conversionOptions);
151119
}).toThrow(SyntaxError);
152120
});
153121
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {IBaseModelOptions} from '../../../src/IBaseModelOptions';
2+
import {BaseModel} from '../../../src';
3+
4+
export class AdminModel extends BaseModel {
5+
public id: string = '';
6+
public name: string = '';
7+
public description: string = '';
8+
public userCount: number = 0;
9+
10+
constructor(data: Partial<AdminModel> = {}, opts: IBaseModelOptions = {}) {
11+
super(opts);
12+
13+
this.update(data);
14+
}
15+
16+
public update(data: Partial<AdminModel>): void {
17+
super.update(data);
18+
}
19+
}

__tests__/data/models/InfoModel.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import {IBaseModelOptions} from '../../../src/IBaseModelOptions';
2-
import {BaseModel} from '../../../src';
2+
import {IConversionOption} from '../../../src/IConversionOption';
3+
import {BaseModel, ConversionTypeEnum} from '../../../src';
4+
import {AdminModel} from './AdminModel';
35

46
export class InfoModel extends BaseModel {
57
public seed: string = '';
68
public results: number | null = null;
79
public page: number | null = null;
810
public version: string = '';
11+
public stringified: AdminModel = AdminModel as any;
912

1013
constructor(data: Partial<InfoModel> = {}, opts: IBaseModelOptions = {}) {
1114
super(opts);
@@ -14,6 +17,10 @@ export class InfoModel extends BaseModel {
1417
}
1518

1619
public update(data: Partial<InfoModel>): void {
17-
super.update(data);
20+
const conversionOptions: IConversionOption = {
21+
stringified: ConversionTypeEnum.JSON,
22+
};
23+
24+
super.update(data, conversionOptions);
1825
}
1926
}

__tests__/data/results.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"seed": "abc",
122122
"results": 3,
123123
"page": 1,
124-
"version": "1.1"
124+
"version": "1.1",
125+
"stringified": "{\"id\":\"fb55b892-65c6-4a2f-9ce1-13ddb3ad6d98\",\"name\":\"Admin\",\"description\":\"Administrator\",\"userCount\":0}"
125126
}
126127
}

package.json

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,29 @@
4646
"dependencies": {},
4747
"license": "MIT",
4848
"devDependencies": {
49-
"@babel/core": "7.2.2",
50-
"@babel/plugin-proposal-class-properties": "7.3.0",
51-
"@babel/plugin-proposal-object-rest-spread": "7.3.1",
52-
"@babel/plugin-transform-runtime": "7.2.0",
53-
"@babel/preset-env": "7.3.1",
54-
"@babel/preset-typescript": "7.1.0",
55-
"@babel/runtime": "7.3.1",
56-
"@types/jest": "23.3.13",
57-
"husky": "1.3.1",
58-
"jest": "24.0.0",
59-
"prettier": "1.16.2",
60-
"pretty-quick": "1.10.0",
49+
"@babel/core": "7.4.5",
50+
"@babel/plugin-proposal-class-properties": "7.4.4",
51+
"@babel/plugin-proposal-object-rest-spread": "7.4.4",
52+
"@babel/plugin-transform-runtime": "7.4.4",
53+
"@babel/preset-env": "7.4.5",
54+
"@babel/preset-typescript": "7.3.3",
55+
"@babel/runtime": "7.4.5",
56+
"@types/jest": "24.0.15",
57+
"husky": "2.4.1",
58+
"jest": "24.8.0",
59+
"prettier": "1.18.2",
60+
"pretty-quick": "1.11.1",
6161
"rimraf": "2.6.3",
62-
"rollup": "1.1.2",
62+
"rollup": "1.16.1",
6363
"rollup-plugin-babel": "4.3.2",
64-
"rollup-plugin-commonjs": "9.2.0",
65-
"rollup-plugin-node-resolve": "4.0.0",
64+
"rollup-plugin-commonjs": "10.0.0",
65+
"rollup-plugin-node-resolve": "5.0.3",
6666
"rollup-plugin-uglify": "6.0.2",
67-
"ts-jest": "23.10.5",
68-
"tslib": "1.9.3",
69-
"tslint": "5.12.1",
70-
"tslint-config-prettier": "1.17.0",
71-
"typescript": "3.2.4"
67+
"ts-jest": "24.0.2",
68+
"tslib": "1.10.0",
69+
"tslint": "5.18.0",
70+
"tslint-config-prettier": "1.18.0",
71+
"typescript": "3.5.2"
7272
},
7373
"author": {
7474
"name": "Robert S. (codeBelt)",

src/BaseModel.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {IBaseModelOptions} from './IBaseModelOptions';
33
import {IBaseModel} from './IBaseModel';
44
import {IConversionOption} from './IConversionOption';
55
import {Util} from './Util';
6+
import {ConversionTypeEnum} from './ConversionTypeEnum';
67

78
/**
89
* Base Model is a design pattern used to transfer data between software application subsystems.
@@ -113,18 +114,24 @@ export class BaseModel extends BaseObject implements IBaseModel {
113114
public update(data: any = {}, conversionOptions: IConversionOption = {}): any {
114115
const dataToUse: {[propertyName: string]: any} = this._isObject(data, true) ? data : {};
115116

117+
Util.validConversionOptionNames(this, conversionOptions);
118+
116119
Object.keys(this).forEach((propertyName: string) => {
117120
// Ignore the sjsId property because it is set in the BaseObject constructor and we don't want to update it.
118121
if (propertyName !== 'sjsId' && propertyName !== 'sjsOptions') {
119122
const currentPropertyData: any = this[propertyName];
120-
const passedInDataForProperty: any = dataToUse[propertyName];
123+
let passedInDataForProperty: any = dataToUse[propertyName];
124+
125+
if (Boolean(conversionOptions[propertyName])) {
126+
const conversionType: ConversionTypeEnum = conversionOptions[propertyName];
127+
128+
passedInDataForProperty = Util.convertDataToConversionType(passedInDataForProperty, conversionType);
129+
}
121130

122131
this[propertyName] = this._getPropertyData(currentPropertyData, passedInDataForProperty);
123132
}
124133
});
125134

126-
Util.convertDataUsingConversionOptions(this, conversionOptions);
127-
128135
return this;
129136
}
130137

src/Util.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,9 @@ export class Util {
144144
return !(normalized == null || normalized <= 0 || normalized === 'false' || normalized === 'off');
145145
}
146146

147-
public static convertDataUsingConversionOptions(data: object, conversionOptions: IConversionOption): void {
147+
public static validConversionOptionNames(data: object, conversionOptions: IConversionOption = {}): void {
148148
Object.keys(conversionOptions).forEach((conversionPropertyName: string) => {
149-
if (data.hasOwnProperty(conversionPropertyName)) {
150-
const propertyData: number | string | boolean = data[conversionPropertyName];
151-
const conversionType: ConversionTypeEnum = conversionOptions[conversionPropertyName];
152-
153-
data[conversionPropertyName] = Util.convertDataToConversionType(propertyData, conversionType);
154-
} else {
149+
if (data.hasOwnProperty(conversionPropertyName) === false) {
155150
throw new SyntaxError(`Conversion property name "${conversionPropertyName}" does not match a property name on the model.`);
156151
}
157152
});
@@ -178,6 +173,10 @@ export class Util {
178173
try {
179174
return JSON.parse(propertyData as string);
180175
} catch (error) {
176+
// Don't throw or console.log errors because it's by design to return the data on error.
177+
// For example the first time the model is created it needs to parse the data but if the
178+
// models update method is called we want to keep the data the same.
179+
181180
return propertyData;
182181
}
183182
default:

0 commit comments

Comments
 (0)