orc-shared 5.7.0-dev.0 → 5.7.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/components/MaterialUI/DataDisplay/Modal.js +3 -3
  2. package/dist/components/MaterialUI/DataDisplay/TransferList.js +2 -3
  3. package/dist/components/MaterialUI/Inputs/Checkbox.js +3 -4
  4. package/dist/components/MaterialUI/Inputs/CheckboxGroup.js +5 -6
  5. package/dist/components/MaterialUI/Inputs/InputBase.js +11 -0
  6. package/dist/components/MaterialUI/Inputs/Select.js +1 -2
  7. package/dist/components/MaterialUI/Inputs/StandaloneRadio.js +2 -3
  8. package/dist/components/MaterialUI/Inputs/createInput.js +3 -4
  9. package/dist/components/MaterialUI/muiThemes.js +10 -11
  10. package/dist/components/Routing/SubPage.js +5 -4
  11. package/dist/constants.js +53 -1
  12. package/dist/hocs/withToggle.js +1 -2
  13. package/dist/hooks/useDispatchWithErrorHandling.js +24 -9
  14. package/dist/reducers/authentication.js +3 -0
  15. package/dist/sharedMessages.js +8 -0
  16. package/dist/utils/modelValidationHelper.js +5 -6
  17. package/dist/utils/propertyBagHelper.js +265 -0
  18. package/dist/utils/responseProcessingHelper.js +17 -3
  19. package/package.json +1 -1
  20. package/src/components/MaterialUI/Inputs/InputBase.js +18 -0
  21. package/src/components/MaterialUI/Inputs/InputBase.test.js +156 -0
  22. package/src/components/Routing/SubPage.js +6 -5
  23. package/src/constants.js +48 -0
  24. package/src/hooks/useDispatchWithErrorHandling.js +16 -2
  25. package/src/hooks/useDispatchWithErrorHandling.test.js +15 -13
  26. package/src/reducers/authentication.js +5 -0
  27. package/src/reducers/authentication.test.js +81 -0
  28. package/src/sharedMessages.js +8 -0
  29. package/src/translations/en-US.json +2 -0
  30. package/src/translations/fr-CA.json +2 -0
  31. package/src/utils/propertyBagHelper.js +239 -0
  32. package/src/utils/propertyBagHelper.test.js +709 -0
  33. package/src/utils/responseProcessingHelper.js +28 -3
  34. package/src/utils/responseProcessingHelper.test.js +256 -12
@@ -2,11 +2,13 @@ import React from "react";
2
2
  import { useDispatchWithErrorHandling } from "./useDispatchWithErrorHandling";
3
3
  import sinon from "sinon";
4
4
  import { mount } from "enzyme";
5
- import { Provider } from "react-redux";
6
5
  import Immutable from "immutable";
7
6
  import { pushGlobalErrorMessage } from "../actions/globalErrorMessages";
7
+ import { extractMessages, TestWrapper } from "../utils/testUtils";
8
+ import sharedMessages from "../sharedMessages";
8
9
 
9
10
  const delay = () => new Promise(resolve => setTimeout(resolve, 10));
11
+ const messages = extractMessages(sharedMessages);
10
12
 
11
13
  describe("useDispatchWithErrorHandling", () => {
12
14
  let store, state, dispatchSpy;
@@ -38,9 +40,9 @@ describe("useDispatchWithErrorHandling", () => {
38
40
  };
39
41
 
40
42
  const component = (
41
- <Provider store={store}>
43
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
42
44
  <TestComp />
43
- </Provider>
45
+ </TestWrapper>
44
46
  );
45
47
 
46
48
  mount(component);
@@ -65,9 +67,9 @@ describe("useDispatchWithErrorHandling", () => {
65
67
  };
66
68
 
67
69
  const component = (
68
- <Provider store={store}>
70
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
69
71
  <TestComp />
70
- </Provider>
72
+ </TestWrapper>
71
73
  );
72
74
 
73
75
  mount(component);
@@ -91,9 +93,9 @@ describe("useDispatchWithErrorHandling", () => {
91
93
  };
92
94
 
93
95
  const component = (
94
- <Provider store={store}>
96
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
95
97
  <TestComp />
96
- </Provider>
98
+ </TestWrapper>
97
99
  );
98
100
 
99
101
  mount(component);
@@ -117,9 +119,9 @@ describe("useDispatchWithErrorHandling", () => {
117
119
  };
118
120
 
119
121
  const component = (
120
- <Provider store={store}>
122
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
121
123
  <TestComp />
122
- </Provider>
124
+ </TestWrapper>
123
125
  );
124
126
 
125
127
  mount(component);
@@ -148,9 +150,9 @@ describe("useDispatchWithErrorHandling", () => {
148
150
  };
149
151
 
150
152
  const component = (
151
- <Provider store={store}>
153
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
152
154
  <TestComp />
153
- </Provider>
155
+ </TestWrapper>
154
156
  );
155
157
 
156
158
  mount(component);
@@ -200,9 +202,9 @@ describe("useDispatchWithErrorHandling", () => {
200
202
  };
201
203
 
202
204
  const component = (
203
- <Provider store={store}>
205
+ <TestWrapper provider={{ store }} intlProvider={{ messages }}>
204
206
  <TestComp />
205
- </Provider>
207
+ </TestWrapper>
206
208
  );
207
209
 
208
210
  mount(component);
@@ -14,6 +14,11 @@ const authenticationReducer = (state = initialState, action) => {
14
14
  s.set("upn", action.payload.upn);
15
15
  s.set("name", action.payload.name);
16
16
  const claims = action.payload.rolesClaimsValues;
17
+
18
+ if (action.meta && action.meta.overrideClaims) {
19
+ s.set("rolesClaimsValues", Immutable.fromJS({}));
20
+ }
21
+
17
22
  claims.forEach(claim => {
18
23
  const [app, scope, role] = claim.split("/");
19
24
  s.setIn(["rolesClaimsValues", app, scope, role], true);
@@ -81,6 +81,87 @@ describe("authentication", () => {
81
81
  });
82
82
  });
83
83
 
84
+ describe("storing authentication with override", () => {
85
+ it("clears existing roles and claims if override is true", () => {
86
+ const oldState = Immutable.fromJS({
87
+ upn: "oldupn",
88
+ name: "oldname",
89
+ rolesClaimsValues: {
90
+ Customer: { "*": { Reader: true } },
91
+ Generic: { "*": { Reader: true } },
92
+ },
93
+ });
94
+ const action = {
95
+ type: GET_AUTHENTICATION_PROFILE_SUCCESS,
96
+ payload: {
97
+ upn: "newupn",
98
+ name: "newname",
99
+ rolesClaimsValues: ["Setting/*/Reader", "Tenant/*/Reader"],
100
+ },
101
+ meta: { overrideClaims: true },
102
+ };
103
+ const newState = reducer(oldState, action);
104
+ return expect(newState, "to have value at", ["rolesClaimsValues"], Immutable.fromJS({
105
+ Setting: { "*": { Reader: true } },
106
+ Tenant: { "*": { Reader: true } },
107
+ }));
108
+ });
109
+
110
+ it("does not clear existing roles and claims if override is false", () => {
111
+ const oldState = Immutable.fromJS({
112
+ upn: "oldupn",
113
+ name: "oldname",
114
+ rolesClaimsValues: {
115
+ Customer: { "*": { Reader: true } },
116
+ Generic: { "*": { Reader: true } },
117
+ },
118
+ });
119
+ const action = {
120
+ type: GET_AUTHENTICATION_PROFILE_SUCCESS,
121
+ payload: {
122
+ upn: "newupn",
123
+ name: "newname",
124
+ rolesClaimsValues: ["Setting/*/Reader", "Tenant/*/Reader"],
125
+ },
126
+ meta: { overrideClaims: false },
127
+ };
128
+ const newState = reducer(oldState, action);
129
+ return expect(newState, "to have value at", ["rolesClaimsValues"], Immutable.fromJS({
130
+ Customer: { "*": { Reader: true } },
131
+ Generic: { "*": { Reader: true } },
132
+ Setting: { "*": { Reader: true } },
133
+ Tenant: { "*": { Reader: true } },
134
+ }));
135
+ });
136
+
137
+ it("does not clear existing roles and claims if override is not provided", () => {
138
+ const oldState = Immutable.fromJS({
139
+ upn: "oldupn",
140
+ name: "oldname",
141
+ rolesClaimsValues: {
142
+ Customer: { "*": { Reader: true } },
143
+ Generic: { "*": { Reader: true } },
144
+ },
145
+ });
146
+ const action = {
147
+ type: GET_AUTHENTICATION_PROFILE_SUCCESS,
148
+ payload: {
149
+ upn: "newupn",
150
+ name: "newname",
151
+ rolesClaimsValues: ["Setting/*/Reader", "Tenant/*/Reader"],
152
+ },
153
+ // meta: { overrideClaims: false }, // Intentionally omitted
154
+ };
155
+ const newState = reducer(oldState, action);
156
+ return expect(newState, "to have value at", ["rolesClaimsValues"], Immutable.fromJS({
157
+ Customer: { "*": { Reader: true } },
158
+ Generic: { "*": { Reader: true } },
159
+ Setting: { "*": { Reader: true } },
160
+ Tenant: { "*": { Reader: true } },
161
+ }));
162
+ });
163
+ });
164
+
84
165
  describe("sign out", () => {
85
166
  const { location } = window;
86
167
  beforeAll(() => {
@@ -271,6 +271,14 @@ const sharedMessages = defineMessages({
271
271
  id: "orc-shared.taskLogs",
272
272
  defaultMessage: "Logs",
273
273
  },
274
+ valueTypeWrapperTrue: {
275
+ id: "orc-shared.valueTypeWrapperTrue",
276
+ defaultMessage: "True",
277
+ },
278
+ valueTypeWrapperFalse: {
279
+ id: "orc-shared.valueTypeWrapperFalse",
280
+ defaultMessage: "False",
281
+ },
274
282
  });
275
283
 
276
284
  export default sharedMessages;
@@ -65,5 +65,7 @@
65
65
  "orc-shared.taskStatus": "Status",
66
66
  "orc-shared.unsavedChanges": "This entity has unsaved changes. All changes will be lost.\n\nAre you sure you want to close it?",
67
67
  "orc-shared.valueName": "Value Name",
68
+ "orc-shared.valueTypeWrapperFalse": "False",
69
+ "orc-shared.valueTypeWrapperTrue": "True",
68
70
  "orc-shared.yes": "Yes"
69
71
  }
@@ -65,5 +65,7 @@
65
65
  "orc-shared.taskStatus": "Statut",
66
66
  "orc-shared.unsavedChanges": "Ce dossier contient des modifications qui ne sont sauvegardées. Si vous quittez, les modifications seront perdues.\n\nVoulez vous vraiment quitter?",
67
67
  "orc-shared.valueName": "Nom de la valeur",
68
+ "orc-shared.valueTypeWrapperFalse": "Faux",
69
+ "orc-shared.valueTypeWrapperTrue": "Vrai",
68
70
  "orc-shared.yes": "Oui"
69
71
  }
@@ -0,0 +1,239 @@
1
+ import {
2
+ attributeDataType,
3
+ dotNetDataTypes,
4
+ jsonCargoType,
5
+ propertyBagPrimitiveDataType,
6
+ serializationTypeKey,
7
+ } from "../constants";
8
+ import { isObject } from "./propertyValidator";
9
+ import sharedMessages from "../sharedMessages";
10
+ import { parseGuid } from "./parseHelper";
11
+
12
+ export const customDataType = {
13
+ money: "Money",
14
+ priceTieredRateTable: "PriceTieredRateTable",
15
+ quantityTieredRateTable: "QuantityTieredRateTable",
16
+ password: "Password",
17
+ carrierProviderSelector: "CarrierProviderSelector",
18
+ routingProviderSelector: "RoutingProviderSelector",
19
+ multipleCarrierProvidersSelector: "MultipleCarrierProvidersSelector",
20
+ };
21
+
22
+ const tieredAttributeTypes = [customDataType.priceTieredRateTable, customDataType.quantityTieredRateTable];
23
+
24
+ export const isTieredAttribute = attribute => tieredAttributeTypes.includes(attribute.customDataType);
25
+
26
+ const buildValueOfType = cargoType => {
27
+ return `ValueOf${cargoType}`;
28
+ };
29
+
30
+ const buildValueContainerOfType = dotNetType => {
31
+ return `Orckestra.Overture.Entities.ValueContainer\`1[[${dotNetType}, mscorlib]], Orckestra.Overture.Entities`;
32
+ };
33
+
34
+ export const createJsonCargo = (cargoType, value) => {
35
+ return {
36
+ __type: buildValueOfType(cargoType),
37
+ value: value,
38
+ };
39
+ };
40
+
41
+ const createTieredTableJsonCargo = tiers => {
42
+ return {
43
+ __type: "Orckestra.Overture.Entities.Orders.TieredRateTable, Orckestra.Overture.Entities",
44
+ tiers: tiers.filter(tier => (tier?.min ?? "") !== ""),
45
+ };
46
+ };
47
+
48
+ export const toJsonCargo = (attribute, value) => {
49
+ switch (attribute.dataType) {
50
+ case attributeDataType.text:
51
+ case attributeDataType.lookup:
52
+ return value;
53
+
54
+ case attributeDataType.boolean:
55
+ return createJsonCargo(jsonCargoType.boolean, value);
56
+
57
+ case attributeDataType.integer:
58
+ return createJsonCargo(jsonCargoType.integer, Number(formatNumber(value, 0)));
59
+
60
+ case attributeDataType.decimal:
61
+ return createJsonCargo(jsonCargoType.double, Number(value));
62
+
63
+ case attributeDataType.dateTime:
64
+ return createJsonCargo(jsonCargoType.dateTime, value);
65
+
66
+ case attributeDataType.customType:
67
+ switch (attribute.customDataType) {
68
+ case customDataType.money:
69
+ return createJsonCargo(jsonCargoType.double, Number(formatNumber(value, 2)));
70
+ case customDataType.priceTieredRateTable:
71
+ case customDataType.quantityTieredRateTable:
72
+ return createTieredTableJsonCargo(value);
73
+ case customDataType.password:
74
+ case customDataType.carrierProviderSelector: // To be properly handled when user story 61801 will be addressed
75
+ case customDataType.routingProviderSelector: // To be properly handled when user story 61801 will be addressed
76
+ case customDataType.multipleCarrierProvidersSelector: // To be properly handled when user story 61801 will be addressed
77
+ return value;
78
+ default:
79
+ throw new Error(`toJsonCargo: attribute.customDataType ${attribute.customDataType} is not implemented`);
80
+ }
81
+ case attributeDataType.entityReference:
82
+ return createJsonCargo(jsonCargoType.entityReferences, value);
83
+ default:
84
+ throw new Error(`toJsonCargo: attribute.dataType ${attribute.dataType} is not implemented`);
85
+ }
86
+ };
87
+
88
+ const dataTypesChecker = {
89
+ booleanDataTypes: [
90
+ buildValueOfType(propertyBagPrimitiveDataType.boolean),
91
+ buildValueContainerOfType(dotNetDataTypes.boolean),
92
+ ],
93
+ integerDataTypes: [
94
+ buildValueOfType(propertyBagPrimitiveDataType.byte),
95
+ buildValueContainerOfType(dotNetDataTypes.byte),
96
+ buildValueOfType(propertyBagPrimitiveDataType.sbyte),
97
+ buildValueContainerOfType(dotNetDataTypes.sbyte),
98
+ buildValueOfType(propertyBagPrimitiveDataType.int16),
99
+ buildValueContainerOfType(dotNetDataTypes.int16),
100
+ buildValueOfType(propertyBagPrimitiveDataType.uint16),
101
+ buildValueContainerOfType(dotNetDataTypes.uint16),
102
+ buildValueOfType(propertyBagPrimitiveDataType.int32),
103
+ buildValueContainerOfType(dotNetDataTypes.int32),
104
+ buildValueOfType(propertyBagPrimitiveDataType.uint32),
105
+ buildValueContainerOfType(dotNetDataTypes.uint32),
106
+ buildValueOfType(propertyBagPrimitiveDataType.int64),
107
+ buildValueContainerOfType(dotNetDataTypes.int64),
108
+ buildValueOfType(propertyBagPrimitiveDataType.uint64),
109
+ buildValueContainerOfType(dotNetDataTypes.uint64),
110
+ ],
111
+ textDataTypes: [
112
+ buildValueOfType(propertyBagPrimitiveDataType.char),
113
+ buildValueContainerOfType(dotNetDataTypes.char),
114
+ buildValueOfType(propertyBagPrimitiveDataType.string),
115
+ buildValueContainerOfType(dotNetDataTypes.string),
116
+ ],
117
+ decimalDataTypes: [
118
+ buildValueOfType(propertyBagPrimitiveDataType.single),
119
+ buildValueContainerOfType(dotNetDataTypes.single),
120
+ buildValueOfType(propertyBagPrimitiveDataType.double),
121
+ buildValueContainerOfType(dotNetDataTypes.double),
122
+ buildValueOfType(propertyBagPrimitiveDataType.decimal),
123
+ buildValueContainerOfType(dotNetDataTypes.decimal),
124
+ ],
125
+ dateTimeDataTypes: [
126
+ buildValueOfType(propertyBagPrimitiveDataType.dateTime),
127
+ buildValueContainerOfType(dotNetDataTypes.dateTime),
128
+ ],
129
+ guidDataTypes: [buildValueOfType(propertyBagPrimitiveDataType.guid), buildValueContainerOfType(dotNetDataTypes.guid)],
130
+
131
+ isBoolean: type => dataTypesChecker.booleanDataTypes.includes(type),
132
+ isInteger: type => dataTypesChecker.integerDataTypes.includes(type),
133
+ isText: type => dataTypesChecker.textDataTypes.includes(type),
134
+ isDecimal: type => dataTypesChecker.decimalDataTypes.includes(type),
135
+ isDateTime: type => dataTypesChecker.dateTimeDataTypes.includes(type),
136
+ isGuid: type => dataTypesChecker.guidDataTypes.includes(type),
137
+ };
138
+
139
+ export const getPropertyBagFormattedPrimitiveValue = (propertyBagValue, formatMessage, formatDate, formatTime) => {
140
+ if (propertyBagValue === null || propertyBagValue === undefined || !isObject(propertyBagValue)) {
141
+ return propertyBagValue;
142
+ }
143
+
144
+ if (propertyBagValue.value === null || propertyBagValue.value === undefined) {
145
+ return null;
146
+ }
147
+
148
+ if (dataTypesChecker.isBoolean(propertyBagValue[serializationTypeKey])) {
149
+ return Boolean(propertyBagValue.value)
150
+ ? formatMessage(sharedMessages.valueTypeWrapperTrue)
151
+ : formatMessage(sharedMessages.valueTypeWrapperFalse);
152
+ }
153
+
154
+ if (dataTypesChecker.isInteger(propertyBagValue[serializationTypeKey])) {
155
+ return formatNumber(propertyBagValue.value, 0);
156
+ }
157
+
158
+ if (dataTypesChecker.isText(propertyBagValue[serializationTypeKey])) {
159
+ return propertyBagValue.value.toString();
160
+ }
161
+
162
+ if (dataTypesChecker.isDecimal(propertyBagValue[serializationTypeKey])) {
163
+ return formatNumber(propertyBagValue.value, 2);
164
+ }
165
+
166
+ if (dataTypesChecker.isDateTime(propertyBagValue[serializationTypeKey])) {
167
+ return `${formatDate(propertyBagValue.value)} ${formatTime(propertyBagValue.value)}`;
168
+ }
169
+
170
+ if (dataTypesChecker.isGuid(propertyBagValue[serializationTypeKey])) {
171
+ return parseGuid(propertyBagValue.value);
172
+ }
173
+
174
+ // we ignore other data types since we have no way to format them
175
+ return null;
176
+ };
177
+
178
+ export const formatNumber = (value, precision) => Number(value).toFixed(precision);
179
+
180
+ export const fixPropertyBagModifiedModel = (model, ...fields) => {
181
+ const fixProfileOperations = model => {
182
+ if (model.hasOwnProperty("profileOperations")) {
183
+ let { profilesToAdd, profilesToUpdate } = model.profileOperations;
184
+ if (profilesToAdd) {
185
+ const items = Array.isArray(profilesToAdd) ? profilesToAdd : Object.values(profilesToAdd);
186
+ items.forEach(pr => {
187
+ fixPropertyBagEmptyValues(pr.propertyBag);
188
+ });
189
+ }
190
+ if (profilesToUpdate) {
191
+ const items = Array.isArray(profilesToUpdate) ? profilesToUpdate : Object.values(profilesToUpdate);
192
+ items.forEach(pr => {
193
+ fixPropertyBagEmptyValues(pr.propertyBag);
194
+ });
195
+ }
196
+ }
197
+ };
198
+
199
+ fixPropertyBagEmptyValues(model.propertyBag);
200
+ fixProfileOperations(model);
201
+
202
+ fields.forEach(field => {
203
+ let fieldValue = model[field]?.value;
204
+ if (!fieldValue) return;
205
+ if (Array.isArray(fieldValue)) {
206
+ fieldValue.forEach(fieldItem => {
207
+ fixPropertyBagEmptyValues(fieldItem.propertyBag);
208
+ fixProfileOperations(fieldItem);
209
+ });
210
+ } else {
211
+ fixPropertyBagEmptyValues(fieldValue.propertyBag);
212
+ }
213
+ });
214
+ };
215
+
216
+ export const fixPropertyBagEmptyValues = propertyBag => {
217
+ if (!propertyBag) return;
218
+ //we need to pass null to backend to remove attribute instead of empty string
219
+ //in case if attribute has validation for min length and when we will pass empty string, then we will get error from platform
220
+ //in case if DateTime attribute has empty value Backend will transform it to some '01/01/1900' date, so we need to pass null
221
+ const modifiedFields = Object.keys(propertyBag);
222
+ modifiedFields.forEach(propertyName => {
223
+ let property = propertyBag[propertyName];
224
+ let propertyValue = property && isObject(property) && property.hasOwnProperty("value") ? property.value : property;
225
+ if (propertyValue === "") {
226
+ propertyBag[propertyName] = null;
227
+ }
228
+ });
229
+ };
230
+
231
+ export const setRequiredBooleansDefault = (attributes, propertyBag) => {
232
+ const requiredBools = attributes.filter(a => a.isRequired && a.dataType === attributeDataType.boolean);
233
+
234
+ requiredBools.forEach(attr => {
235
+ if (!propertyBag[attr.name]) {
236
+ propertyBag[attr.name] = toJsonCargo(attr, false);
237
+ }
238
+ });
239
+ };