@northern.tech/store 0.1.0

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 (284) hide show
  1. package/.eslintrc.cjs +9 -0
  2. package/.prettierignore +1 -0
  3. package/CHANGELOG.md +12 -0
  4. package/actions.ts +32 -0
  5. package/api/general-api.test.ts +51 -0
  6. package/api/general-api.ts +60 -0
  7. package/api/types/AWSCredentials.ts +16 -0
  8. package/api/types/Actor.ts +35 -0
  9. package/api/types/Addon.ts +17 -0
  10. package/api/types/AddonTenantadm.ts +17 -0
  11. package/api/types/Alert.ts +34 -0
  12. package/api/types/AlertDetails.ts +23 -0
  13. package/api/types/AlertSubject.ts +23 -0
  14. package/api/types/ApiBurst.ts +21 -0
  15. package/api/types/ApiBurstTenantadm.ts +21 -0
  16. package/api/types/ApiLimits.ts +16 -0
  17. package/api/types/ApiLimitsTenantadm.ts +16 -0
  18. package/api/types/ApiQuota.ts +17 -0
  19. package/api/types/ApiQuotaTenantadm.ts +17 -0
  20. package/api/types/Artifact.ts +48 -0
  21. package/api/types/ArtifactDeployments.ts +48 -0
  22. package/api/types/ArtifactInfo.ts +11 -0
  23. package/api/types/ArtifactInfoDeployments.ts +11 -0
  24. package/api/types/ArtifactLink.ts +11 -0
  25. package/api/types/ArtifactTypeInfo.ts +13 -0
  26. package/api/types/ArtifactTypeInfoDeployments.ts +13 -0
  27. package/api/types/ArtifactUpdate.ts +10 -0
  28. package/api/types/ArtifactUploadLink.ts +15 -0
  29. package/api/types/Attribute.ts +36 -0
  30. package/api/types/AttributeInventory.ts +32 -0
  31. package/api/types/AuditLog.ts +26 -0
  32. package/api/types/AuthSet.ts +33 -0
  33. package/api/types/AuthSetDeviceauth.ts +37 -0
  34. package/api/types/AutoAuthRequest.ts +23 -0
  35. package/api/types/AzureSharedAccessSecret.ts +11 -0
  36. package/api/types/BillingInfo.ts +13 -0
  37. package/api/types/BinaryDeltaConfiguration.ts +15 -0
  38. package/api/types/BinaryDeltaConfigurationDeployments.ts +15 -0
  39. package/api/types/BinaryDeltaLimits.ts +15 -0
  40. package/api/types/BinaryDeltaLimitsDeployments.ts +15 -0
  41. package/api/types/BoundingBox.ts +11 -0
  42. package/api/types/CancelRequest.ts +10 -0
  43. package/api/types/CardData.ts +49 -0
  44. package/api/types/CardSetupData.ts +17 -0
  45. package/api/types/CheckoutData.ts +13 -0
  46. package/api/types/Configuration.ts +11 -0
  47. package/api/types/Count.ts +13 -0
  48. package/api/types/Credentials.ts +23 -0
  49. package/api/types/DBusSubsystem.ts +27 -0
  50. package/api/types/DeltaConfiguration.ts +14 -0
  51. package/api/types/DeltaConfigurationDeployments.ts +14 -0
  52. package/api/types/Deployment.ts +95 -0
  53. package/api/types/DeploymentAggregation.ts +18 -0
  54. package/api/types/DeploymentAggregationItem.ts +18 -0
  55. package/api/types/DeploymentAggregationTerm.ts +23 -0
  56. package/api/types/DeploymentAggregationTerms.ts +18 -0
  57. package/api/types/DeploymentAttributeProjection.ts +11 -0
  58. package/api/types/DeploymentAuditlogs.ts +11 -0
  59. package/api/types/DeploymentDeployments.ts +99 -0
  60. package/api/types/DeploymentFilterTerm.ts +37 -0
  61. package/api/types/DeploymentIdentifier.ts +10 -0
  62. package/api/types/DeploymentPhase.ts +23 -0
  63. package/api/types/DeploymentReporting.ts +46 -0
  64. package/api/types/DeploymentSearchTerms.ts +39 -0
  65. package/api/types/DeploymentSortTerm.ts +25 -0
  66. package/api/types/DeploymentStatistics.ts +15 -0
  67. package/api/types/DeploymentStatusStatistics.ts +54 -0
  68. package/api/types/Device.ts +29 -0
  69. package/api/types/DeviceAggregation.ts +18 -0
  70. package/api/types/DeviceAggregationItem.ts +18 -0
  71. package/api/types/DeviceAggregationTerm.ts +27 -0
  72. package/api/types/DeviceAggregationTerms.ts +22 -0
  73. package/api/types/DeviceAttribute.ts +23 -0
  74. package/api/types/DeviceAttributeProjection.ts +15 -0
  75. package/api/types/DeviceAuditlogs.ts +17 -0
  76. package/api/types/DeviceAuthEvent.ts +23 -0
  77. package/api/types/DeviceConfiguration.ts +16 -0
  78. package/api/types/DeviceDeployment.ts +11 -0
  79. package/api/types/DeviceDeployments.ts +31 -0
  80. package/api/types/DeviceDeviceauth.ts +42 -0
  81. package/api/types/DeviceFilterAttribute.ts +22 -0
  82. package/api/types/DeviceFilterTerm.ts +41 -0
  83. package/api/types/DeviceInventory.ts +19 -0
  84. package/api/types/DeviceInventoryInventory.ts +19 -0
  85. package/api/types/DeviceReporting.ts +22 -0
  86. package/api/types/DeviceSearchTerms.ts +39 -0
  87. package/api/types/DeviceSortTerm.ts +29 -0
  88. package/api/types/DeviceState.ts +32 -0
  89. package/api/types/DeviceStateDeviceconnect.ts +33 -0
  90. package/api/types/DeviceStateIot_manager.ts +14 -0
  91. package/api/types/DeviceStatus.ts +19 -0
  92. package/api/types/DeviceWithImage.ts +80 -0
  93. package/api/types/DirectUploadMetadata.ts +18 -0
  94. package/api/types/EmailVerificationCompletion.ts +13 -0
  95. package/api/types/EmailVerificationRequest.ts +13 -0
  96. package/api/types/Error.ts +17 -0
  97. package/api/types/ErrorAuditlogs.ts +13 -0
  98. package/api/types/ErrorDeployments.ts +17 -0
  99. package/api/types/ErrorDeviceauth.ts +13 -0
  100. package/api/types/ErrorDeviceconfig.ts +17 -0
  101. package/api/types/ErrorDeviceconnect.ts +17 -0
  102. package/api/types/ErrorDevicemonitor.ts +17 -0
  103. package/api/types/ErrorExt.ts +18 -0
  104. package/api/types/ErrorInventory.ts +17 -0
  105. package/api/types/ErrorIot_manager.ts +17 -0
  106. package/api/types/ErrorNotFound.ts +17 -0
  107. package/api/types/ErrorReporting.ts +18 -0
  108. package/api/types/ErrorTenantadm.ts +13 -0
  109. package/api/types/ErrorUseradm.ts +17 -0
  110. package/api/types/Event.ts +48 -0
  111. package/api/types/ExternalIdentity.ts +17 -0
  112. package/api/types/Features.ts +17 -0
  113. package/api/types/FileUpload.ts +23 -0
  114. package/api/types/Filter.ts +19 -0
  115. package/api/types/FilterAttribute.ts +21 -0
  116. package/api/types/FilterDefinition.ts +18 -0
  117. package/api/types/FilterDeployments.ts +19 -0
  118. package/api/types/FilterInventory.ts +20 -0
  119. package/api/types/FilterPredicate.ts +78 -0
  120. package/api/types/FilterPredicateDeployments.ts +48 -0
  121. package/api/types/FilterPredicateInventory.ts +76 -0
  122. package/api/types/GeoBoundingBox.ts +10 -0
  123. package/api/types/GeoBoundingBoxFilter.ts +6 -0
  124. package/api/types/GeoDistance.ts +17 -0
  125. package/api/types/GeoDistanceFilter.ts +10 -0
  126. package/api/types/GeoPoint.ts +15 -0
  127. package/api/types/Group.ts +10 -0
  128. package/api/types/HTTP.ts +19 -0
  129. package/api/types/IdentityData.ts +25 -0
  130. package/api/types/InputParameter.ts +8 -0
  131. package/api/types/Integration.ts +24 -0
  132. package/api/types/JobStatus.ts +13 -0
  133. package/api/types/Limit.ts +21 -0
  134. package/api/types/LimitDeployments.ts +21 -0
  135. package/api/types/LimitDeviceauth.ts +10 -0
  136. package/api/types/LimitUseradm.ts +11 -0
  137. package/api/types/Limits.ts +11 -0
  138. package/api/types/LineDescriptor.ts +11 -0
  139. package/api/types/LogSubsystem.ts +23 -0
  140. package/api/types/LoginOptions.ts +18 -0
  141. package/api/types/ManagementAPIConfiguration.ts +5 -0
  142. package/api/types/MenderTypes.ts +183 -0
  143. package/api/types/MonitorConfiguration.ts +22 -0
  144. package/api/types/NewConfigurationDeployment.ts +15 -0
  145. package/api/types/NewConfigurationDeploymentResponse.ts +10 -0
  146. package/api/types/NewDeployment.ts +42 -0
  147. package/api/types/NewDeploymentForGroup.ts +33 -0
  148. package/api/types/NewDeploymentPhase.ts +37 -0
  149. package/api/types/NewDeploymentPhaseDeployments.ts +20 -0
  150. package/api/types/NewDeploymentV2.ts +31 -0
  151. package/api/types/NewTenant.ts +41 -0
  152. package/api/types/Object.ts +35 -0
  153. package/api/types/OwnUserUpdate.ts +21 -0
  154. package/api/types/PasswordResetCompletion.ts +17 -0
  155. package/api/types/PasswordResetRequest.ts +13 -0
  156. package/api/types/Permission.ts +21 -0
  157. package/api/types/PermissionObject.ts +14 -0
  158. package/api/types/PermissionSet.ts +22 -0
  159. package/api/types/PermissionSetWithScope.ts +15 -0
  160. package/api/types/PersonalAccessToken.ts +29 -0
  161. package/api/types/PersonalAccessTokenRequest.ts +18 -0
  162. package/api/types/Plan.ts +19 -0
  163. package/api/types/PlanBindingDetails.ts +13 -0
  164. package/api/types/PlanChangeRequest.ts +14 -0
  165. package/api/types/PlanLimits.ts +12 -0
  166. package/api/types/PreAuthSet.ts +16 -0
  167. package/api/types/Release.ts +32 -0
  168. package/api/types/ReleaseDeployments.ts +32 -0
  169. package/api/types/ReleaseUpdate.ts +13 -0
  170. package/api/types/Releases.ts +9 -0
  171. package/api/types/ReleasesDeleteError.ts +21 -0
  172. package/api/types/ReleasesDeployments.ts +9 -0
  173. package/api/types/Role.ts +19 -0
  174. package/api/types/RolePermission.ts +25 -0
  175. package/api/types/RolePermissionObject.ts +17 -0
  176. package/api/types/RoleUseradm.ts +19 -0
  177. package/api/types/SAMLMetadata.ts +21 -0
  178. package/api/types/Scope.ts +19 -0
  179. package/api/types/SelectAttribute.ts +17 -0
  180. package/api/types/ServiceSubsystem.ts +19 -0
  181. package/api/types/Settings.ts +23 -0
  182. package/api/types/SortCriteria.ts +30 -0
  183. package/api/types/SsoObject.ts +21 -0
  184. package/api/types/Status.ts +17 -0
  185. package/api/types/StatusDeviceauth.ts +18 -0
  186. package/api/types/StatusTenantadm.ts +19 -0
  187. package/api/types/StorageLimit.ts +17 -0
  188. package/api/types/SubscriptionData.ts +8 -0
  189. package/api/types/SupportRequest.ts +17 -0
  190. package/api/types/Tag.ts +27 -0
  191. package/api/types/Tags.ts +10 -0
  192. package/api/types/TaskResult.ts +13 -0
  193. package/api/types/TaskResultCLI.ts +10 -0
  194. package/api/types/TaskResultHTTPRequest.ts +8 -0
  195. package/api/types/TaskResultHTTPResponse.ts +10 -0
  196. package/api/types/Tenant.ts +38 -0
  197. package/api/types/TenantApiLimits.ts +12 -0
  198. package/api/types/TenantApiLimitsTenantadm.ts +12 -0
  199. package/api/types/TenantIdName.ts +14 -0
  200. package/api/types/TenantIdsData.ts +10 -0
  201. package/api/types/TenantInfo.ts +14 -0
  202. package/api/types/TenantShortInfo.ts +21 -0
  203. package/api/types/TenantTenantadm.ts +38 -0
  204. package/api/types/TenantsIdName.ts +6 -0
  205. package/api/types/Update.ts +17 -0
  206. package/api/types/UpdateDeployments.ts +18 -0
  207. package/api/types/UpdateFile.ts +13 -0
  208. package/api/types/UpdateFileDeployments.ts +13 -0
  209. package/api/types/UpdateTypes.ts +8 -0
  210. package/api/types/UpgradeCompleteRequest.ts +23 -0
  211. package/api/types/User.ts +13 -0
  212. package/api/types/UserAuditlogs.ts +13 -0
  213. package/api/types/UserNew.ts +26 -0
  214. package/api/types/UserUpdate.ts +25 -0
  215. package/api/types/UserUseradm.ts +45 -0
  216. package/api/types/UserWithTenantInfo.ts +8 -0
  217. package/api/types/XDeltaArgs.ts +9 -0
  218. package/api/types/XDeltaArgsDeployments.ts +9 -0
  219. package/api/types/XDeltaArgsLimits.ts +10 -0
  220. package/api/types/XDeltaArgsLimitsDeployments.ts +10 -0
  221. package/api/users-api.ts +34 -0
  222. package/appSlice/constants.ts +166 -0
  223. package/appSlice/index.ts +159 -0
  224. package/appSlice/reducer.test.ts +97 -0
  225. package/appSlice/selectors.ts +33 -0
  226. package/appSlice/thunks.test.ts +118 -0
  227. package/appSlice/thunks.ts +148 -0
  228. package/auth.test.ts +36 -0
  229. package/auth.ts +82 -0
  230. package/commonConstants.tsx +252 -0
  231. package/commonSelectors.ts +255 -0
  232. package/constants.ts +22 -0
  233. package/deploymentsSlice/constants.ts +175 -0
  234. package/deploymentsSlice/index.ts +145 -0
  235. package/deploymentsSlice/reducer.test.ts +108 -0
  236. package/deploymentsSlice/selectors.ts +58 -0
  237. package/deploymentsSlice/thunks.test.ts +425 -0
  238. package/deploymentsSlice/thunks.ts +382 -0
  239. package/devicesSlice/constants.ts +56 -0
  240. package/devicesSlice/index.ts +207 -0
  241. package/devicesSlice/reducer.test.ts +194 -0
  242. package/devicesSlice/selectors.ts +127 -0
  243. package/devicesSlice/thunks.test.tsx +1245 -0
  244. package/devicesSlice/thunks.tsx +1098 -0
  245. package/monitorSlice/constants.ts +17 -0
  246. package/monitorSlice/index.ts +65 -0
  247. package/monitorSlice/reducer.test.ts +79 -0
  248. package/monitorSlice/selectors.ts +15 -0
  249. package/monitorSlice/thunks.test.ts +112 -0
  250. package/monitorSlice/thunks.ts +101 -0
  251. package/onboardingSlice/constants.ts +53 -0
  252. package/onboardingSlice/index.ts +60 -0
  253. package/onboardingSlice/reducer.test.ts +45 -0
  254. package/onboardingSlice/selectors.ts +15 -0
  255. package/onboardingSlice/thunks.test.ts +235 -0
  256. package/onboardingSlice/thunks.ts +142 -0
  257. package/organizationSlice/constants.ts +82 -0
  258. package/organizationSlice/index.ts +111 -0
  259. package/organizationSlice/reducer.test.ts +78 -0
  260. package/organizationSlice/selectors.ts +45 -0
  261. package/organizationSlice/thunks.test.ts +619 -0
  262. package/organizationSlice/thunks.ts +383 -0
  263. package/package.json +38 -0
  264. package/releasesSlice/constants.ts +18 -0
  265. package/releasesSlice/index.ts +116 -0
  266. package/releasesSlice/reducer.test.ts +78 -0
  267. package/releasesSlice/selectors.ts +33 -0
  268. package/releasesSlice/thunks.test.ts +389 -0
  269. package/releasesSlice/thunks.ts +379 -0
  270. package/selectors.ts +22 -0
  271. package/store.ts +89 -0
  272. package/storehooks.test.tsx +386 -0
  273. package/storehooks.ts +250 -0
  274. package/thunks.ts +21 -0
  275. package/tsconfig.json +11 -0
  276. package/tsup.config.ts +11 -0
  277. package/usersSlice/constants.ts +374 -0
  278. package/usersSlice/index.ts +151 -0
  279. package/usersSlice/reducer.test.ts +141 -0
  280. package/usersSlice/selectors.ts +60 -0
  281. package/usersSlice/thunks.test.ts +660 -0
  282. package/usersSlice/thunks.ts +773 -0
  283. package/utils.test.ts +187 -0
  284. package/utils.ts +249 -0
@@ -0,0 +1,386 @@
1
+ // Copyright 2024 Northern.tech AS
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // @ts-nocheck
15
+ import React from 'react';
16
+ import { Provider } from 'react-redux';
17
+
18
+ import {
19
+ getDeploymentsByStatus,
20
+ getDeviceAttributes,
21
+ getDeviceLimit,
22
+ getDevicesByStatus,
23
+ getDevicesWithAuth,
24
+ getDynamicGroups,
25
+ getGroups,
26
+ getIntegrations,
27
+ getReleases,
28
+ getUserOrganization,
29
+ tenantDataDivergedMessage
30
+ } from '@northern.tech/store/thunks';
31
+ import { act, renderHook } from '@testing-library/react';
32
+ import configureMockStore from 'redux-mock-store';
33
+ import { thunk } from 'redux-thunk';
34
+
35
+ import { inventoryDevice } from '../../../tests/__mocks__/deviceHandlers';
36
+ import { defaultState, receivedPermissionSets, receivedRoles, userId } from '../../../tests/mockData';
37
+ import { actions as appActions } from './appSlice';
38
+ import { getLatestReleaseInfo, setOfflineThreshold } from './appSlice/thunks';
39
+ import { latestSaasReleaseTag } from './appSlice/thunks.test';
40
+ import { getSessionInfo } from './auth';
41
+ import { EXTERNAL_PROVIDER, TIMEOUTS, UNGROUPED_GROUP, timeUnits } from './commonConstants';
42
+ import { DEVICE_STATES } from './constants';
43
+ import { actions as deploymentsActions } from './deploymentsSlice';
44
+ import { actions as deviceActions } from './devicesSlice';
45
+ import { actions as onboardingActions } from './onboardingSlice';
46
+ import { defaultOnboardingState, expectedOnboardingActions } from './onboardingSlice/thunks.test';
47
+ import { actions as organizationActions } from './organizationSlice';
48
+ import { actions as releasesActions } from './releasesSlice';
49
+ import { useAppInit } from './storehooks';
50
+ import { actions as userActions } from './usersSlice';
51
+ import { getGlobalSettings, getPermissionSets, getRoles, getUserSettings, saveUserSettings } from './usersSlice/thunks';
52
+
53
+ const middlewares = [thunk];
54
+ const mockStore = configureMockStore(middlewares);
55
+
56
+ const attributeReducer = (accu, item) => {
57
+ if (item.scope === 'inventory') {
58
+ accu[item.name] = item.value;
59
+ if (item.name === 'device_type') {
60
+ accu[item.name] = [].concat(item.value);
61
+ }
62
+ }
63
+ return accu;
64
+ };
65
+
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
+ const { attributes, ...expectedDevice } = defaultState.devices.byId.a1;
68
+ export const receivedInventoryDevice = {
69
+ ...defaultState.devices.byId.a1,
70
+ attributes: inventoryDevice.attributes.reduce(attributeReducer, {}),
71
+ identity_data: { ...defaultState.devices.byId.a1.identity_data, status: DEVICE_STATES.accepted },
72
+ isNew: false,
73
+ isOffline: true,
74
+ monitor: {},
75
+ tags: {},
76
+ updated_ts: inventoryDevice.updated_ts
77
+ };
78
+
79
+ const appInitActions = [
80
+ { type: userActions.successfullyLoggedIn.type }, //, payload: { token }
81
+ { type: onboardingActions.setOnboardingComplete.type, payload: false },
82
+ { type: onboardingActions.setDemoArtifactPort.type, payload: 85 },
83
+ { type: appActions.setFeatures.type, payload: { ...defaultState.app.features, hasMultitenancy: true } },
84
+ {
85
+ type: appActions.setVersionInformation.type,
86
+ payload: {
87
+ docsVersion: '',
88
+ value: {
89
+ Deployments: '1.2.3',
90
+ Deviceauth: null,
91
+ GUI: undefined,
92
+ Integration: 'master',
93
+ Inventory: null,
94
+ 'Mender-Artifact': undefined,
95
+ 'Mender-Client': 'next',
96
+ 'Meta-Mender': 'saas-123.34'
97
+ }
98
+ }
99
+ },
100
+ {
101
+ type: appActions.setEnvironmentData.type,
102
+ payload: { feedbackProbability: 0.3, hostAddress: null, hostedAnnouncement: '', recaptchaSiteKey: '', stripeAPIKey: '', trackerCode: '' }
103
+ },
104
+ { type: getLatestReleaseInfo.pending.type },
105
+ { type: getUserSettings.pending.type },
106
+ { type: getGlobalSettings.pending.type },
107
+ { type: getDeviceAttributes.pending.type },
108
+ { type: getDeploymentsByStatus.pending.type },
109
+ { type: getDeploymentsByStatus.pending.type },
110
+ { type: getDevicesByStatus.pending.type },
111
+ { type: getDevicesByStatus.pending.type },
112
+ { type: getDevicesByStatus.pending.type },
113
+ { type: getDevicesByStatus.pending.type },
114
+ { type: getDynamicGroups.pending.type },
115
+ { type: getGroups.pending.type },
116
+ { type: getIntegrations.pending.type },
117
+ { type: getReleases.pending.type },
118
+ { type: getDeviceLimit.pending.type },
119
+ { type: getRoles.pending.type },
120
+ { type: getPermissionSets.pending.type },
121
+ { type: appActions.setFirstLoginAfterSignup.type, payload: false },
122
+ { type: getUserOrganization.pending.type },
123
+ { type: deploymentsActions.receivedDeployments.type, payload: defaultState.deployments.byId },
124
+ {
125
+ type: deploymentsActions.receivedDeploymentsForStatus.type,
126
+ payload: { deploymentIds: Object.keys(defaultState.deployments.byId), status: 'finished', total: Object.keys(defaultState.deployments.byId).length }
127
+ },
128
+ { type: deploymentsActions.receivedDeployments.type, payload: defaultState.deployments.byId },
129
+ {
130
+ type: deploymentsActions.receivedDeploymentsForStatus.type,
131
+ payload: { deploymentIds: Object.keys(defaultState.deployments.byId), status: 'inprogress', total: Object.keys(defaultState.deployments.byId).length }
132
+ },
133
+ {
134
+ type: deploymentsActions.selectDeploymentsForStatus.type,
135
+ payload: { deploymentIds: Object.keys(defaultState.deployments.byId), status: 'inprogress', total: Object.keys(defaultState.deployments.byId).length }
136
+ },
137
+ { type: getDeploymentsByStatus.fulfilled.type },
138
+ { type: getDeploymentsByStatus.fulfilled.type },
139
+ { type: deviceActions.setDeviceLimit.type, payload: 500 },
140
+ { type: getDeviceLimit.fulfilled.type },
141
+ {
142
+ type: deviceActions.receivedGroups.type,
143
+ payload: {
144
+ testGroup: defaultState.devices.groups.byId.testGroup,
145
+ testGroupDynamic: { filters: [{ key: 'group', operator: '$eq', scope: 'system', value: 'things' }], id: 'filter1' }
146
+ }
147
+ },
148
+ { type: getDevicesByStatus.pending.type },
149
+ { type: deviceActions.setFilterAttributes.type },
150
+ { type: getDeviceAttributes.fulfilled.type },
151
+ {
152
+ type: deviceActions.receivedGroups.type,
153
+ payload: {
154
+ testGroup: defaultState.devices.groups.byId.testGroup,
155
+ testGroupDynamic: {
156
+ deviceIds: [],
157
+ filters: [
158
+ { key: 'id', operator: '$in', scope: 'identity', value: [defaultState.devices.byId.a1.id] },
159
+ { key: 'mac', operator: '$nexists', scope: 'identity', value: false },
160
+ { key: 'kernel', operator: '$exists', scope: 'identity', value: true }
161
+ ],
162
+ id: 'filter1',
163
+ total: 0
164
+ }
165
+ }
166
+ },
167
+ { type: getDynamicGroups.fulfilled.type },
168
+ {
169
+ type: deviceActions.receivedDevices.type,
170
+ payload: {
171
+ [defaultState.devices.byId.a1.id]: { ...receivedInventoryDevice, group: 'test' },
172
+ [defaultState.devices.byId.b1.id]: {
173
+ ...receivedInventoryDevice,
174
+ id: defaultState.devices.byId.b1.id,
175
+ group: 'test',
176
+ identity_data: { ...defaultState.devices.byId.b1.identity_data, status: DEVICE_STATES.accepted }
177
+ }
178
+ }
179
+ },
180
+ {
181
+ type: deviceActions.setDevicesByStatus.type,
182
+ payload: {
183
+ deviceIds: [defaultState.devices.byId.a1.id, defaultState.devices.byId.b1.id],
184
+ status: DEVICE_STATES.accepted,
185
+ total: defaultState.devices.byStatus.accepted.deviceIds.length
186
+ }
187
+ },
188
+ { type: getDevicesWithAuth.pending.type },
189
+ { type: deviceActions.receivedDevices.type, payload: { [expectedDevice.id]: { ...receivedInventoryDevice, group: 'test', status: 'pending' } } },
190
+ {
191
+ type: deviceActions.setDevicesByStatus.type,
192
+ payload: {
193
+ deviceIds: Array.from({ length: defaultState.devices.byStatus.pending.total }, () => defaultState.devices.byId.a1.id),
194
+ status: DEVICE_STATES.pending,
195
+ total: defaultState.devices.byStatus.pending.deviceIds.length
196
+ }
197
+ },
198
+ { type: getDevicesWithAuth.pending.type },
199
+ { type: deviceActions.receivedDevices.type, payload: {} },
200
+ { type: deviceActions.setDevicesByStatus.type, payload: { deviceIds: [], status: 'preauthorized', total: 0 } },
201
+ { type: deviceActions.receivedDevices.type, payload: {} },
202
+ { type: deviceActions.setDevicesByStatus.type, payload: { deviceIds: [], status: 'rejected', total: 0 } },
203
+ {
204
+ type: appActions.setVersionInformation.type,
205
+ payload: {
206
+ GUI: latestSaasReleaseTag,
207
+ Integration: '1.2.3',
208
+ 'Mender-Artifact': '1.3.7',
209
+ 'Mender-Client': '3.2.1',
210
+ backend: latestSaasReleaseTag,
211
+ latestRelease: {
212
+ releaseDate: '2022-02-02',
213
+ repos: {
214
+ integration: '1.2.3',
215
+ mender: '3.2.1',
216
+ 'mender-artifact': '1.3.7',
217
+ 'other-service': '1.1.0',
218
+ service: '3.0.0'
219
+ }
220
+ }
221
+ }
222
+ },
223
+ { type: organizationActions.setOrganization.type, payload: defaultState.organization.organization },
224
+ { type: appActions.setAnnouncement.type, payload: tenantDataDivergedMessage },
225
+ { type: getDevicesByStatus.fulfilled.type },
226
+ { type: getDevicesByStatus.fulfilled.type },
227
+ { type: getLatestReleaseInfo.fulfilled.type },
228
+ { type: getUserOrganization.fulfilled.type },
229
+ {
230
+ type: organizationActions.receiveExternalDeviceIntegrations.type,
231
+ payload: [
232
+ { connection_string: 'something_else', id: 1, provider: EXTERNAL_PROVIDER['iot-hub'].provider },
233
+ { id: 2, provider: EXTERNAL_PROVIDER['iot-core'].provider, something: 'new' }
234
+ ]
235
+ },
236
+ { type: getIntegrations.fulfilled.type },
237
+ { type: releasesActions.receiveReleases.type, payload: defaultState.releases.byId },
238
+ {
239
+ type: releasesActions.setReleaseListState.type,
240
+ payload: { ...defaultState.releases.releasesList, releaseIds: [defaultState.releases.byId.r1.name], page: 42 }
241
+ },
242
+ { type: getReleases.fulfilled.type },
243
+ {
244
+ type: deviceActions.receivedDevices.type,
245
+ payload: {
246
+ [expectedDevice.id]: { ...defaultState.devices.byId.a1, group: undefined, isNew: false, isOffline: true, monitor: {}, tags: {} },
247
+ [defaultState.devices.byId.b1.id]: { ...defaultState.devices.byId.b1, group: undefined, isNew: false, isOffline: true, monitor: {}, tags: {} }
248
+ }
249
+ },
250
+ {
251
+ type: deviceActions.receivedDevices.type,
252
+ payload: {
253
+ [expectedDevice.id]: { ...defaultState.devices.byId.a1, group: undefined, isNew: false, isOffline: true, monitor: {}, tags: {} }
254
+ }
255
+ },
256
+ { type: getDevicesWithAuth.fulfilled.type },
257
+ { type: getDevicesWithAuth.fulfilled.type },
258
+ {
259
+ type: deviceActions.receivedDevices.type,
260
+ payload: {
261
+ [expectedDevice.id]: { ...receivedInventoryDevice, group: 'test' },
262
+ [defaultState.devices.byId.b1.id]: { ...receivedInventoryDevice, id: defaultState.devices.byId.b1.id, group: 'test' }
263
+ }
264
+ },
265
+ { type: getDevicesWithAuth.pending.type },
266
+ { type: getDevicesByStatus.fulfilled.type },
267
+ { type: getDevicesByStatus.fulfilled.type },
268
+ { type: userActions.setGlobalSettings.type, payload: { ...defaultState.users.globalSettings } },
269
+ { type: setOfflineThreshold.pending.type },
270
+ { type: appActions.setOfflineThreshold.type, payload: '2019-01-12T13:00:06.900Z' },
271
+ { type: setOfflineThreshold.fulfilled.type },
272
+ { type: userActions.setUserSettings.type, payload: { ...defaultState.users.userSettings } },
273
+ { type: getGlobalSettings.fulfilled.type },
274
+ { type: getUserSettings.fulfilled.type },
275
+ { type: userActions.receivedPermissionSets.type, payload: receivedPermissionSets },
276
+ { type: getPermissionSets.fulfilled.type },
277
+ { type: userActions.receivedRoles.type, payload: receivedRoles },
278
+ { type: getRoles.fulfilled.type },
279
+ {
280
+ type: deviceActions.receivedDevices.type,
281
+ payload: {
282
+ [defaultState.devices.byId.a1.id]: { ...defaultState.devices.byId.a1, group: undefined, isNew: false, isOffline: true, monitor: {}, tags: {} },
283
+ [defaultState.devices.byId.b1.id]: { ...defaultState.devices.byId.b1, group: undefined, isNew: false, isOffline: true, monitor: {}, tags: {} }
284
+ }
285
+ },
286
+ { type: getDevicesWithAuth.fulfilled.type },
287
+ { type: getDevicesByStatus.fulfilled.type },
288
+ {
289
+ type: deviceActions.addGroup.type,
290
+ payload: {
291
+ groupName: UNGROUPED_GROUP.id,
292
+ group: {
293
+ filters: [{ key: 'group', operator: '$nin', scope: 'system', value: [Object.keys(defaultState.devices.groups.byId)[0]] }]
294
+ }
295
+ }
296
+ },
297
+ { type: getGroups.fulfilled.type },
298
+ { type: deviceActions.setDeviceListState.type, payload: { selectedAttributes: [] } },
299
+ { type: userActions.setTooltipsState.type, payload: {} },
300
+ { type: saveUserSettings.pending.type },
301
+ { type: getUserSettings.pending.type },
302
+ { type: userActions.setUserSettings.type, payload: { ...defaultState.users.userSettings } },
303
+ { type: getUserSettings.fulfilled.type },
304
+ { type: userActions.setUserSettings.type, payload: { ...defaultState.users.userSettings, onboarding: defaultOnboardingState } },
305
+ { type: saveUserSettings.fulfilled.type },
306
+ ...expectedOnboardingActions
307
+ ];
308
+
309
+ it('should try to get all required app information', async () => {
310
+ const store = mockStore({
311
+ ...defaultState,
312
+ app: { ...defaultState.app, features: { ...defaultState.app.features, isHosted: true } },
313
+ users: {
314
+ ...defaultState.users,
315
+ currentSession: getSessionInfo(),
316
+ globalSettings: { ...defaultState.users.globalSettings, id_attribute: { attribute: 'mac', scope: 'identity' } }
317
+ },
318
+ releases: { ...defaultState.releases, releasesList: { ...defaultState.releases.releasesList, page: 42 } }
319
+ });
320
+ const wrapper = ({ children }) => <Provider store={store}>{children}</Provider>;
321
+ renderHook(() => useAppInit(userId), { wrapper });
322
+ await act(async () => {
323
+ jest.runAllTimers();
324
+ jest.runAllTicks();
325
+ });
326
+
327
+ const storeActions = store.getActions();
328
+ expect(storeActions.length).toEqual(appInitActions.length);
329
+ appInitActions.map((action, index) => Object.keys(action).map(key => expect(storeActions[index][key]).toEqual(action[key])));
330
+ });
331
+ it('should execute the offline threshold migration for multi day thresholds', async () => {
332
+ const store = mockStore({
333
+ ...defaultState,
334
+ app: { ...defaultState.app, features: { ...defaultState.app.features, isHosted: true } },
335
+ users: {
336
+ ...defaultState.users,
337
+ currentSession: getSessionInfo(),
338
+ globalSettings: {
339
+ ...defaultState.users.globalSettings,
340
+ id_attribute: { attribute: 'mac', scope: 'identity' },
341
+ offlineThreshold: { interval: 48, intervalUnit: timeUnits.hours }
342
+ }
343
+ },
344
+ releases: { ...defaultState.releases, releasesList: { ...defaultState.releases.releasesList, page: 42 } }
345
+ });
346
+ const wrapper = ({ children }) => <Provider store={store}>{children}</Provider>;
347
+ renderHook(() => useAppInit(userId), { wrapper });
348
+ await act(async () => {
349
+ jest.runAllTimers();
350
+ jest.runAllTicks();
351
+ });
352
+
353
+ const storeActions = store.getActions();
354
+ expect(storeActions.length).toEqual(appInitActions.length + 9); // 3 = get settings + set settings + set offline threshold
355
+ const settingStorageAction = storeActions.find(action => action.type === userActions.setGlobalSettings.type && action.payload.offlineThreshold);
356
+ expect(settingStorageAction.payload.offlineThreshold.interval).toEqual(2);
357
+ expect(settingStorageAction.payload.offlineThreshold.intervalUnit).toEqual(timeUnits.days);
358
+ });
359
+ it('should trigger the offline threshold migration dialog', async () => {
360
+ const store = mockStore({
361
+ ...defaultState,
362
+ app: { ...defaultState.app, features: { ...defaultState.app.features, isHosted: true } },
363
+ users: {
364
+ ...defaultState.users,
365
+ currentSession: getSessionInfo(),
366
+ globalSettings: {
367
+ ...defaultState.users.globalSettings,
368
+ id_attribute: { attribute: 'mac', scope: 'identity' },
369
+ offlineThreshold: { interval: 15, intervalUnit: 'minutes' }
370
+ }
371
+ },
372
+ releases: { ...defaultState.releases, releasesList: { ...defaultState.releases.releasesList, page: 42 } }
373
+ });
374
+
375
+ const wrapper = ({ children }) => <Provider store={store}>{children}</Provider>;
376
+ renderHook(() => useAppInit(userId), { wrapper });
377
+ await jest.advanceTimersByTimeAsync(TIMEOUTS.fiveSeconds + TIMEOUTS.oneSecond);
378
+ await jest.runAllTimersAsync();
379
+ await act(async () => {
380
+ jest.runAllTicks();
381
+ });
382
+ const storeActions = store.getActions();
383
+ expect(storeActions.length).toEqual(appInitActions.length + 1); // only setShowStartupNotification should be addded
384
+ const notificationAction = storeActions.find(action => action.type === userActions.setShowStartupNotification.type);
385
+ expect(notificationAction.payload).toBeTruthy();
386
+ });
package/storehooks.ts ADDED
@@ -0,0 +1,250 @@
1
+ // Copyright 2024 Northern.tech AS
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ // @ts-nocheck
15
+ import { useCallback, useEffect, useRef } from 'react';
16
+ import { useDispatch, useSelector } from 'react-redux';
17
+
18
+ import dayjs from 'dayjs';
19
+ import durationDayJs from 'dayjs/plugin/duration';
20
+ import Cookies from 'universal-cookie';
21
+
22
+ import storeActions from './actions';
23
+ import { getSessionInfo } from './auth';
24
+ import { DEPLOYMENT_STATES, DEVICE_STATES, TIMEOUTS, timeUnits } from './constants';
25
+ import {
26
+ getDevicesByStatus as getDevicesByStatusSelector,
27
+ getFeatures,
28
+ getGlobalSettings as getGlobalSettingsSelector,
29
+ getIsEnterprise,
30
+ getOfflineThresholdSettings,
31
+ getOnboardingState as getOnboardingStateSelector,
32
+ getSortedFilteringAttributes,
33
+ getUserCapabilities,
34
+ getUserSettings as getUserSettingsSelector
35
+ } from './selectors';
36
+ import {
37
+ getDeploymentsByStatus,
38
+ getDeviceAttributes,
39
+ getDeviceById,
40
+ getDeviceLimit,
41
+ getDevicesByStatus,
42
+ getDynamicGroups,
43
+ getGlobalSettings,
44
+ getGroups,
45
+ getIntegrations,
46
+ getLatestReleaseInfo,
47
+ getOnboardingState,
48
+ getReleases,
49
+ getRoles,
50
+ getUserOrganization,
51
+ getUserSettings,
52
+ saveGlobalSettings,
53
+ saveUserSettings
54
+ } from './thunks';
55
+ import { extractErrorMessage, getComparisonCompatibleVersion, stringToBoolean } from './utils';
56
+
57
+ const cookies = new Cookies();
58
+ dayjs.extend(durationDayJs);
59
+
60
+ const { setDeviceListState, setFirstLoginAfterSignup, setTooltipsState, setShowStartupNotification } = storeActions;
61
+
62
+ const featureFlags = [
63
+ 'hasAuditlogs',
64
+ 'hasMultitenancy',
65
+ 'hasDeltaProgress',
66
+ 'hasDeviceConfig',
67
+ 'hasDeviceConnect',
68
+ 'hasFeedbackEnabled',
69
+ 'hasReporting',
70
+ 'hasMonitor',
71
+ 'isEnterprise'
72
+ ];
73
+
74
+ const environmentDatas = ['feedbackProbability', 'hostAddress', 'hostedAnnouncement', 'recaptchaSiteKey', 'stripeAPIKey', 'trackerCode'];
75
+
76
+ export const parseEnvironmentInfo = () => (dispatch, getState) => {
77
+ const state = getState();
78
+ let onboardingComplete = state.onboarding.complete || !!JSON.parse(window.localStorage.getItem('onboardingComplete') ?? 'false');
79
+ let demoArtifactPort = 85;
80
+ let environmentData = {};
81
+ let environmentFeatures = {};
82
+ let versionInfo = {};
83
+ if (mender_environment) {
84
+ const {
85
+ features = {},
86
+ demoArtifactPort: port,
87
+ disableOnboarding,
88
+ integrationVersion,
89
+ isDemoMode,
90
+ menderVersion,
91
+ menderArtifactVersion,
92
+ metaMenderVersion,
93
+ services = {}
94
+ } = mender_environment;
95
+ onboardingComplete = stringToBoolean(features.isEnterprise) || stringToBoolean(disableOnboarding) || onboardingComplete;
96
+ demoArtifactPort = port || demoArtifactPort;
97
+ environmentData = environmentDatas.reduce((accu, flag) => ({ ...accu, [flag]: mender_environment[flag] || state.app[flag] }), {});
98
+ environmentFeatures = {
99
+ ...featureFlags.reduce((accu, flag) => ({ ...accu, [flag]: stringToBoolean(features[flag]) }), {}),
100
+ isHosted: features.isHosted || window.location.hostname.includes('hosted.mender.io'),
101
+ isDemoMode: stringToBoolean(isDemoMode || features.isDemoMode)
102
+ };
103
+ versionInfo = {
104
+ docs: isNaN(integrationVersion.charAt(0)) ? '' : integrationVersion.split('.').slice(0, 2).join('.'),
105
+ remainder: {
106
+ Integration: getComparisonCompatibleVersion(integrationVersion),
107
+ 'Mender-Client': getComparisonCompatibleVersion(menderVersion),
108
+ 'Mender-Artifact': menderArtifactVersion,
109
+ 'Meta-Mender': metaMenderVersion,
110
+ Deployments: services.deploymentsVersion,
111
+ Deviceauth: services.deviceauthVersion,
112
+ Inventory: services.inventoryVersion,
113
+ GUI: services.guiVersion
114
+ }
115
+ };
116
+ }
117
+ return Promise.all([
118
+ dispatch(storeActions.successfullyLoggedIn(getSessionInfo())),
119
+ dispatch(storeActions.setOnboardingComplete(onboardingComplete)),
120
+ dispatch(storeActions.setDemoArtifactPort(demoArtifactPort)),
121
+ dispatch(storeActions.setFeatures(environmentFeatures)),
122
+ dispatch(storeActions.setVersionInformation({ docsVersion: versionInfo.docs, value: versionInfo.remainder })),
123
+ dispatch(storeActions.setEnvironmentData(environmentData)),
124
+ dispatch(getLatestReleaseInfo())
125
+ ]);
126
+ };
127
+
128
+ const maybeAddOnboardingTasks = ({ devicesByStatus, dispatch, onboardingState, tasks }) => {
129
+ if (!onboardingState.showTips || onboardingState.complete) {
130
+ return tasks;
131
+ }
132
+ // try to retrieve full device details for onboarding devices to ensure ips etc. are available
133
+ // we only load the first few/ 20 devices, as it is possible the onboarding is left dangling
134
+ // and a lot of devices are present and we don't want to flood the backend for this
135
+ return devicesByStatus[DEVICE_STATES.accepted].deviceIds.reduce((accu, id) => {
136
+ accu.push(dispatch(getDeviceById(id)));
137
+ return accu;
138
+ }, tasks);
139
+ };
140
+
141
+ export const useAppInit = userId => {
142
+ const dispatch = useDispatch();
143
+ const isEnterprise = useSelector(getIsEnterprise);
144
+ const { hasMultitenancy, isHosted } = useSelector(getFeatures);
145
+ const devicesByStatus = useSelector(getDevicesByStatusSelector);
146
+ const onboardingState = useSelector(getOnboardingStateSelector);
147
+ let { columnSelection = [], trackingConsentGiven: hasTrackingEnabled, tooltips = {} } = useSelector(getUserSettingsSelector);
148
+ const { canManageUsers } = useSelector(getUserCapabilities);
149
+ const { interval, intervalUnit } = useSelector(getOfflineThresholdSettings);
150
+ const { id_attribute } = useSelector(getGlobalSettingsSelector);
151
+ const { identityAttributes } = useSelector(getSortedFilteringAttributes);
152
+ const initRunning = useRef(false);
153
+
154
+ const retrieveAppData = useCallback(() => {
155
+ let tasks = [
156
+ dispatch(parseEnvironmentInfo()),
157
+ dispatch(getUserSettings()),
158
+ dispatch(getGlobalSettings()),
159
+ dispatch(getDeviceAttributes()),
160
+ dispatch(getDeploymentsByStatus({ status: DEPLOYMENT_STATES.finished, shouldSelect: false })),
161
+ dispatch(getDeploymentsByStatus({ status: DEPLOYMENT_STATES.inprogress })),
162
+ dispatch(getDevicesByStatus({ status: DEVICE_STATES.accepted })),
163
+ dispatch(getDevicesByStatus({ status: DEVICE_STATES.pending })),
164
+ dispatch(getDevicesByStatus({ status: DEVICE_STATES.preauth })),
165
+ dispatch(getDevicesByStatus({ status: DEVICE_STATES.rejected })),
166
+ dispatch(getDynamicGroups()),
167
+ dispatch(getGroups()),
168
+ dispatch(getIntegrations()),
169
+ dispatch(getReleases()),
170
+ dispatch(getDeviceLimit()),
171
+ dispatch(getRoles()),
172
+ dispatch(setFirstLoginAfterSignup(stringToBoolean(cookies.get('firstLoginAfterSignup'))))
173
+ ];
174
+ const multitenancy = hasMultitenancy || isHosted || isEnterprise;
175
+ if (multitenancy) {
176
+ tasks.push(dispatch(getUserOrganization()));
177
+ }
178
+ return Promise.all(tasks);
179
+ }, [dispatch, hasMultitenancy, isHosted, isEnterprise]);
180
+
181
+ const interpretAppData = useCallback(() => {
182
+ let settings = {};
183
+ if (cookies.get('_ga') && typeof hasTrackingEnabled === 'undefined') {
184
+ settings.trackingConsentGiven = true;
185
+ }
186
+ let tasks = [
187
+ dispatch(setDeviceListState({ selectedAttributes: columnSelection.map(column => ({ attribute: column.key, scope: column.scope })) })),
188
+ dispatch(setTooltipsState(tooltips)), // tooltips read state is primarily trusted from the redux store, except on app init - here user settings are the reference
189
+ dispatch(saveUserSettings(settings))
190
+ ];
191
+ // checks if user id is set and if cookie for helptips exists for that user
192
+ tasks = maybeAddOnboardingTasks({ devicesByStatus, dispatch, tasks, onboardingState });
193
+
194
+ if (canManageUsers && intervalUnit && intervalUnit !== timeUnits.days) {
195
+ const duration = dayjs.duration(interval, intervalUnit);
196
+ const days = duration.asDays();
197
+ if (days < 1) {
198
+ tasks.push(Promise.resolve(setTimeout(() => dispatch(setShowStartupNotification(true)), TIMEOUTS.fiveSeconds)));
199
+ } else {
200
+ const roundedDays = Math.max(1, Math.round(days));
201
+ tasks.push(dispatch(saveGlobalSettings({ offlineThreshold: { interval: roundedDays, intervalUnit: timeUnits.days } })));
202
+ }
203
+ }
204
+
205
+ // the following is used as a migration and initialization of the stored identity attribute
206
+ // changing the default device attribute to the first non-deviceId attribute, unless a stored
207
+ // id attribute setting exists
208
+ const identityOptions = identityAttributes.filter(attribute => !['id', 'Device ID', 'status'].includes(attribute));
209
+ if (!id_attribute && identityOptions.length) {
210
+ tasks.push(dispatch(saveGlobalSettings({ id_attribute: { attribute: identityOptions[0], scope: 'identity' } })));
211
+ } else if (typeof id_attribute === 'string') {
212
+ let attribute = id_attribute;
213
+ if (attribute === 'Device ID') {
214
+ attribute = 'id';
215
+ }
216
+ tasks.push(dispatch(saveGlobalSettings({ id_attribute: { attribute, scope: 'identity' } })));
217
+ }
218
+ return Promise.all(tasks);
219
+ }, [
220
+ columnSelection,
221
+ dispatch,
222
+ identityAttributes,
223
+ hasTrackingEnabled,
224
+ canManageUsers,
225
+ devicesByStatus,
226
+ id_attribute,
227
+ interval,
228
+ intervalUnit,
229
+ onboardingState,
230
+ tooltips
231
+ ]);
232
+
233
+ const initializeAppData = useCallback(
234
+ () =>
235
+ retrieveAppData()
236
+ .then(interpretAppData)
237
+ // this is allowed to fail if no user information are available
238
+ .catch(err => console.log(extractErrorMessage(err)))
239
+ .then(() => dispatch(getOnboardingState())),
240
+ [dispatch, retrieveAppData, interpretAppData]
241
+ );
242
+
243
+ useEffect(() => {
244
+ if (!userId || initRunning.current) {
245
+ return;
246
+ }
247
+ initRunning.current = true;
248
+ initializeAppData();
249
+ }, [userId, initializeAppData]);
250
+ };
package/thunks.ts ADDED
@@ -0,0 +1,21 @@
1
+ // Copyright 2024 Northern.tech AS
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ export * from './appSlice/thunks';
15
+ export * from './deploymentsSlice/thunks';
16
+ export * from './devicesSlice/thunks';
17
+ export * from './monitorSlice/thunks';
18
+ export * from './onboardingSlice/thunks';
19
+ export * from './organizationSlice/thunks';
20
+ export * from './releasesSlice/thunks';
21
+ export * from './usersSlice/thunks';
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "@northern.tech/typescript-config/react-app.json",
3
+ "exclude": ["dist", "build", "node_modules", "**/*.test.js"],
4
+ "compilerOptions": {
5
+ "rootDir": ".",
6
+ "baseUrl": ".",
7
+ "paths": {
8
+ "@northern.tech/store/*": ["*"]
9
+ }
10
+ }
11
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ import { peerDependencies } from './package.json';
4
+
5
+ export default defineConfig(options => ({
6
+ format: ['cjs', 'esm'],
7
+ dts: true,
8
+ sourcemap: true,
9
+ external: [...Object.keys(peerDependencies)],
10
+ ...options
11
+ }));