@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,379 @@
1
+ // Copyright 2019 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 storeActions from '@northern.tech/store/actions';
16
+ import GeneralApi from '@northern.tech/store/api/general-api';
17
+ import {
18
+ DEVICE_LIST_DEFAULTS,
19
+ SORTING_OPTIONS,
20
+ TIMEOUTS,
21
+ deploymentsApiUrl,
22
+ deploymentsApiUrlV2,
23
+ emptyFilter,
24
+ headerNames
25
+ } from '@northern.tech/store/constants';
26
+ import { getSearchEndpoint } from '@northern.tech/store/selectors';
27
+ import { commonErrorFallback, commonErrorHandler } from '@northern.tech/store/store';
28
+ import { convertDeviceListStateToFilters, progress } from '@northern.tech/store/utils';
29
+ import { customSort, deepCompare, duplicateFilter, extractSoftwareItem } from '@northern.tech/utils/helpers';
30
+ import { formatReleases } from '@northern.tech/utils/locationutils';
31
+ import { createAsyncThunk } from '@reduxjs/toolkit';
32
+ import { isCancel } from 'axios';
33
+ import { v4 as uuid } from 'uuid';
34
+
35
+ import { actions, sliceName } from '.';
36
+ import { ARTIFACT_GENERATION_TYPE } from './constants';
37
+ import { getReleasesById } from './selectors';
38
+
39
+ const { setSnackbar, initUpload, uploadProgress, cleanUpUpload } = storeActions;
40
+ const { page: defaultPage, perPage: defaultPerPage } = DEVICE_LIST_DEFAULTS;
41
+
42
+ const sortingDefaults = { direction: SORTING_OPTIONS.desc, key: 'modified' };
43
+
44
+ const flattenRelease = (release, stateRelease) => {
45
+ const updatedArtifacts = release.artifacts?.sort(customSort(1, 'modified')) || [];
46
+ const { artifacts, deviceTypes, modified } = updatedArtifacts.reduce(
47
+ (accu, item) => {
48
+ accu.deviceTypes.push(...item.device_types_compatible);
49
+ const stateArtifact = stateRelease.artifacts?.find(releaseArtifact => releaseArtifact.id === item.id) || {};
50
+ accu.modified = accu.modified ? accu.modified : item.modified;
51
+ accu.artifacts.push({
52
+ ...stateArtifact,
53
+ ...item
54
+ });
55
+ return accu;
56
+ },
57
+ { artifacts: [], deviceTypes: [], modified: undefined }
58
+ );
59
+ return { ...stateRelease, ...release, artifacts, device_types_compatible: deviceTypes.filter(duplicateFilter), modified };
60
+ };
61
+
62
+ const reduceReceivedReleases = (releases, stateReleasesById) =>
63
+ releases.reduce((accu, release) => {
64
+ const stateRelease = stateReleasesById[release.name] || {};
65
+ accu[release.name] = flattenRelease(release, stateRelease);
66
+ return accu;
67
+ }, {});
68
+
69
+ const findArtifactIndexInRelease = (releases, id) =>
70
+ Object.values(releases).reduce(
71
+ (accu, item) => {
72
+ let index = item.artifacts.findIndex(releaseArtifact => releaseArtifact.id === id);
73
+ if (index > -1) {
74
+ accu = { release: item, index };
75
+ }
76
+ return accu;
77
+ },
78
+ { release: null, index: -1 }
79
+ );
80
+
81
+ /* Artifacts */
82
+ export const getArtifactInstallCount = createAsyncThunk(`${sliceName}/getArtifactInstallCount`, (id, { dispatch, getState }) => {
83
+ let { release, index } = findArtifactIndexInRelease(getReleasesById(getState()), id);
84
+ if (!release || index === -1) {
85
+ return;
86
+ }
87
+ const releaseArtifacts = [...release.artifacts];
88
+ const artifact = releaseArtifacts[index];
89
+ const { key, name, version } = extractSoftwareItem(artifact.artifact_provides) ?? {};
90
+ const attribute = `${key}${name ? `.${name}` : ''}.version`;
91
+ const { filterTerms } = convertDeviceListStateToFilters({
92
+ filters: [{ ...emptyFilter, key: attribute, value: version, scope: 'inventory' }]
93
+ });
94
+ return GeneralApi.post(getSearchEndpoint(getState()), {
95
+ page: 1,
96
+ per_page: 1,
97
+ filters: filterTerms,
98
+ attributes: [{ scope: 'identity', attribute: 'status' }]
99
+ })
100
+ .catch(err => commonErrorHandler(err, `Retrieving artifact installation count failed:`, dispatch, commonErrorFallback))
101
+ .then(({ headers }) => {
102
+ let { release, index } = findArtifactIndexInRelease(getReleasesById(getState()), id);
103
+ if (!release || index === -1) {
104
+ return;
105
+ }
106
+ const installCount = Number(headers[headerNames.total]);
107
+ const releaseArtifacts = [...release.artifacts];
108
+ releaseArtifacts[index] = { ...releaseArtifacts[index], installCount };
109
+ release = {
110
+ ...release,
111
+ artifacts: releaseArtifacts
112
+ };
113
+ return dispatch(actions.receiveRelease(release));
114
+ });
115
+ });
116
+
117
+ export const getArtifactUrl = createAsyncThunk(`${sliceName}/getArtifactUrl`, (id, { dispatch, getState }) =>
118
+ GeneralApi.get(`${deploymentsApiUrl}/artifacts/${id}/download`).then(response => {
119
+ let { release, index } = findArtifactIndexInRelease(getReleasesById(getState()), id);
120
+ if (!release || index === -1) {
121
+ return dispatch(getReleases());
122
+ }
123
+ const releaseArtifacts = [...release.artifacts];
124
+ releaseArtifacts[index] = {
125
+ ...releaseArtifacts[index],
126
+ url: response.data.uri
127
+ };
128
+ release = {
129
+ ...release,
130
+ artifacts: releaseArtifacts
131
+ };
132
+ return dispatch(actions.receiveRelease(release));
133
+ })
134
+ );
135
+
136
+ export const createArtifact = createAsyncThunk(`${sliceName}/createArtifact`, ({ file, meta }, { dispatch }) => {
137
+ let formData = Object.entries(meta).reduce((accu, [key, value]) => {
138
+ if (Array.isArray(value)) {
139
+ accu.append(key, value.join(','));
140
+ } else if (value instanceof Object) {
141
+ accu.append(key, JSON.stringify(value));
142
+ } else {
143
+ accu.append(key, value);
144
+ }
145
+ return accu;
146
+ }, new FormData());
147
+ formData.append('type', ARTIFACT_GENERATION_TYPE.SINGLE_FILE);
148
+ formData.append('file', file);
149
+ const uploadId = uuid();
150
+ const cancelSource = new AbortController();
151
+ return Promise.all([
152
+ dispatch(setSnackbar('Generating artifact')),
153
+ dispatch(initUpload({ id: uploadId, upload: { name: file.name, size: file.size, uploadProgress: 0, cancelSource } })),
154
+ GeneralApi.upload(
155
+ `${deploymentsApiUrl}/artifacts/generate`,
156
+ formData,
157
+ e => dispatch(uploadProgress({ id: uploadId, progress: progress(e) })),
158
+ cancelSource.signal
159
+ )
160
+ ])
161
+ .then(() => {
162
+ setTimeout(() => {
163
+ dispatch(getReleases());
164
+ dispatch(selectRelease(file.name));
165
+ }, TIMEOUTS.oneSecond);
166
+ return Promise.resolve(dispatch(setSnackbar('Upload successful', TIMEOUTS.fiveSeconds)));
167
+ })
168
+ .catch(err => {
169
+ if (isCancel(err)) {
170
+ return dispatch(setSnackbar('The artifact generation has been cancelled', TIMEOUTS.fiveSeconds));
171
+ }
172
+ return commonErrorHandler(err, `Artifact couldn't be generated.`, dispatch);
173
+ })
174
+ .finally(() => dispatch(cleanUpUpload(uploadId)));
175
+ });
176
+
177
+ export const uploadArtifact = createAsyncThunk(`${sliceName}/uploadArtifact`, ({ file, meta }, { dispatch }) => {
178
+ let formData = new FormData();
179
+ formData.append('size', file.size);
180
+ formData.append('description', meta.description);
181
+ formData.append('artifact', file);
182
+ const uploadId = uuid();
183
+ const cancelSource = new AbortController();
184
+ return Promise.all([
185
+ dispatch(setSnackbar('Uploading artifact')),
186
+ dispatch(initUpload({ id: uploadId, upload: { name: file.name, size: file.size, uploadProgress: 0, cancelSource } })),
187
+ GeneralApi.upload(`${deploymentsApiUrl}/artifacts`, formData, e => dispatch(uploadProgress({ id: uploadId, progress: progress(e) })), cancelSource.signal)
188
+ ])
189
+ .then(() => {
190
+ const tasks = [dispatch(setSnackbar('Upload successful', TIMEOUTS.fiveSeconds)), dispatch(getReleases())];
191
+ if (meta.name) {
192
+ tasks.push(dispatch(selectRelease(meta.name)));
193
+ }
194
+ return Promise.all(tasks);
195
+ })
196
+ .catch(err => {
197
+ if (isCancel(err)) {
198
+ return dispatch(setSnackbar('The upload has been cancelled', TIMEOUTS.fiveSeconds));
199
+ }
200
+ return commonErrorHandler(err, `Artifact couldn't be uploaded.`, dispatch);
201
+ })
202
+ .finally(() => dispatch(cleanUpUpload(uploadId)));
203
+ });
204
+
205
+ export const cancelFileUpload = createAsyncThunk(`${sliceName}/cancelFileUpload`, (id, { dispatch, getState }) => {
206
+ const { [id]: current } = getState().app.uploadsById;
207
+ current.cancelSource.abort();
208
+ return Promise.resolve(dispatch(cleanUpUpload(id)));
209
+ });
210
+
211
+ export const editArtifact = createAsyncThunk(`${sliceName}/editArtifact`, ({ id, body }, { dispatch, getState }) =>
212
+ GeneralApi.put(`${deploymentsApiUrl}/artifacts/${id}`, body)
213
+ .catch(err => commonErrorHandler(err, `Artifact details couldn't be updated.`, dispatch))
214
+ .then(() => {
215
+ const state = getState();
216
+ let { release, index } = findArtifactIndexInRelease(getReleasesById(state), id);
217
+ if (!release || index === -1) {
218
+ return dispatch(getReleases());
219
+ }
220
+ release.artifacts[index].description = body.description;
221
+ return Promise.all([
222
+ dispatch(actions.receiveRelease(release)),
223
+ dispatch(setSnackbar('Artifact details were updated successfully.', TIMEOUTS.fiveSeconds, '')),
224
+ dispatch(getRelease(release.name)),
225
+ dispatch(selectRelease(release.name))
226
+ ]);
227
+ })
228
+ );
229
+
230
+ export const removeArtifact = createAsyncThunk(`${sliceName}/removeArtifact`, (id, { dispatch, getState }) =>
231
+ GeneralApi.delete(`${deploymentsApiUrl}/artifacts/${id}`)
232
+ .then(() => {
233
+ const state = getState();
234
+ let { release, index } = findArtifactIndexInRelease(getReleasesById(state), id);
235
+ const releaseArtifacts = [...release.artifacts];
236
+ releaseArtifacts.splice(index, 1);
237
+ if (!releaseArtifacts.length) {
238
+ const { releasesList } = state.releases;
239
+ const releaseIds = releasesList.releaseIds.filter(id => release.name !== id);
240
+ return Promise.all([
241
+ dispatch(actions.removeRelease(release.name)),
242
+ dispatch(
243
+ setReleasesListState({
244
+ releaseIds,
245
+ searchTotal: releasesList.searchTerm ? releasesList.searchTotal - 1 : releasesList.searchTotal,
246
+ total: releasesList.total - 1
247
+ })
248
+ )
249
+ ]);
250
+ }
251
+ return Promise.all([dispatch(setSnackbar('Artifact was removed', TIMEOUTS.fiveSeconds, '')), dispatch(actions.receiveRelease(release))]);
252
+ })
253
+ .catch(err => commonErrorHandler(err, `Error removing artifact:`, dispatch))
254
+ );
255
+
256
+ export const removeRelease = createAsyncThunk(`${sliceName}/removeRelease`, (releaseId, { dispatch, getState }) =>
257
+ Promise.all(getReleasesById(getState())[releaseId].artifacts.map(({ id }) => dispatch(removeArtifact(id)))).then(() => dispatch(selectRelease()))
258
+ );
259
+
260
+ export const selectRelease = createAsyncThunk(`${sliceName}/selectRelease`, (release, { dispatch }) => {
261
+ const name = release ? release.name || release : null;
262
+ let tasks = [dispatch(actions.selectedRelease(name))];
263
+ if (name) {
264
+ tasks.push(dispatch(getRelease(name)));
265
+ }
266
+ return Promise.all(tasks);
267
+ });
268
+
269
+ export const setReleasesListState = createAsyncThunk(`${sliceName}/setReleasesListState`, (selectionState, { dispatch, getState }) => {
270
+ const currentState = getState().releases.releasesList;
271
+ let nextState = {
272
+ ...currentState,
273
+ ...selectionState,
274
+ sort: { ...currentState.sort, ...selectionState.sort }
275
+ };
276
+ let tasks = [];
277
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
278
+ const { isLoading: currentLoading, ...currentRequestState } = currentState;
279
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
280
+ const { isLoading: selectionLoading, ...selectionRequestState } = nextState;
281
+ if (!deepCompare(currentRequestState, selectionRequestState)) {
282
+ nextState.isLoading = true;
283
+ tasks.push(dispatch(getReleases(nextState)).finally(() => dispatch(setReleasesListState({ isLoading: false }))));
284
+ }
285
+ tasks.push(dispatch(actions.setReleaseListState(nextState)));
286
+ return Promise.all(tasks);
287
+ });
288
+
289
+ /* Releases */
290
+
291
+ const releaseListRetrieval = config => {
292
+ const { searchTerm = '', page = defaultPage, perPage = defaultPerPage, sort = sortingDefaults, selectedTags = [], type = '' } = config;
293
+ const { key: attribute, direction } = sort;
294
+ const filterQuery = formatReleases({ pageState: { searchTerm, selectedTags } });
295
+ const updateType = type ? `update_type=${type}` : '';
296
+ const sorting = attribute ? `sort=${attribute}:${direction}`.toLowerCase() : '';
297
+ return GeneralApi.get(
298
+ `${deploymentsApiUrlV2}/deployments/releases?${[`page=${page}`, `per_page=${perPage}`, filterQuery, updateType, sorting].filter(i => i).join('&')}`
299
+ );
300
+ };
301
+
302
+ const deductSearchState = (receivedReleases, config, total, state) => {
303
+ let releaseListState = { ...state.releasesList };
304
+ const { searchTerm, searchOnly, sort = {}, selectedTags = [], type } = config;
305
+ const flattenedReleases = Object.values(receivedReleases).sort(customSort(sort.direction === SORTING_OPTIONS.desc, sort.key));
306
+ const releaseIds = flattenedReleases.map(item => item.name);
307
+ const isFiltering = !!(selectedTags.length || type || searchTerm);
308
+ if (searchOnly) {
309
+ releaseListState = { ...releaseListState, searchedIds: releaseIds };
310
+ } else {
311
+ releaseListState = {
312
+ ...releaseListState,
313
+ releaseIds,
314
+ searchTotal: isFiltering ? total : state.releasesList.searchTotal,
315
+ total: !isFiltering ? total : state.releasesList.total
316
+ };
317
+ }
318
+ return releaseListState;
319
+ };
320
+
321
+ export const getReleases = createAsyncThunk(`${sliceName}/getReleases`, (passedConfig = {}, { dispatch, getState }) => {
322
+ const config = { ...getState().releases.releasesList, ...passedConfig };
323
+ return releaseListRetrieval(config)
324
+ .then(({ data: receivedReleases = [], headers = {} }) => {
325
+ const total = headers[headerNames.total] ? Number(headers[headerNames.total]) : 0;
326
+ const state = getState().releases;
327
+ const flatReleases = reduceReceivedReleases(receivedReleases, state.byId);
328
+ const combinedReleases = { ...state.byId, ...flatReleases };
329
+ let tasks = [dispatch(actions.receiveReleases(combinedReleases))];
330
+ const releaseListState = deductSearchState(receivedReleases, config, total, state);
331
+ tasks.push(dispatch(actions.setReleaseListState(releaseListState)));
332
+ return Promise.all(tasks);
333
+ })
334
+ .catch(err => commonErrorHandler(err, `Please check your connection`, dispatch));
335
+ });
336
+
337
+ export const getRelease = createAsyncThunk(`${sliceName}/getReleases`, (name, { dispatch, getState }) =>
338
+ releaseListRetrieval({ searchTerm: name, page: 1, perPage: 1 }).then(({ data: releases }) => {
339
+ if (releases.length) {
340
+ const stateRelease = getReleasesById(getState())[releases[0].name] || {};
341
+ return Promise.resolve(dispatch(actions.receiveRelease(flattenRelease(releases[0], stateRelease))));
342
+ }
343
+ return Promise.resolve(null);
344
+ })
345
+ );
346
+
347
+ export const updateReleaseInfo = createAsyncThunk(`${sliceName}/updateReleaseInfo`, ({ name, info }, { dispatch, getState }) =>
348
+ GeneralApi.patch(`${deploymentsApiUrlV2}/deployments/releases/${name}`, info)
349
+ .catch(err => commonErrorHandler(err, `Release details couldn't be updated.`, dispatch))
350
+ .then(() => {
351
+ return Promise.all([
352
+ dispatch(actions.receiveRelease({ ...getReleasesById(getState())[name], ...info, name })),
353
+ dispatch(setSnackbar('Release details were updated successfully.', TIMEOUTS.fiveSeconds, ''))
354
+ ]);
355
+ })
356
+ );
357
+
358
+ export const setReleaseTags = createAsyncThunk(`${sliceName}/setReleaseTags`, ({ name, tags = [] }, { dispatch, getState }) =>
359
+ GeneralApi.put(`${deploymentsApiUrlV2}/deployments/releases/${name}/tags`, tags)
360
+ .catch(err => commonErrorHandler(err, `Release tags couldn't be set.`, dispatch))
361
+ .then(() => {
362
+ return Promise.all([
363
+ dispatch(actions.receiveRelease({ ...getReleasesById(getState())[name], name, tags })),
364
+ dispatch(setSnackbar('Release tags were set successfully.', TIMEOUTS.fiveSeconds, ''))
365
+ ]);
366
+ })
367
+ );
368
+
369
+ export const getExistingReleaseTags = createAsyncThunk(`${sliceName}/getReleaseTags`, (_, { dispatch }) =>
370
+ GeneralApi.get(`${deploymentsApiUrlV2}/releases/all/tags`)
371
+ .catch(err => commonErrorHandler(err, `Existing release tags couldn't be retrieved.`, dispatch))
372
+ .then(({ data: tags }) => Promise.resolve(dispatch(actions.receiveReleaseTags(tags))))
373
+ );
374
+
375
+ export const getUpdateTypes = createAsyncThunk(`${sliceName}/getReleaseTypes`, (_, { dispatch }) =>
376
+ GeneralApi.get(`${deploymentsApiUrlV2}/releases/all/types`)
377
+ .catch(err => commonErrorHandler(err, `Existing update types couldn't be retrieved.`, dispatch))
378
+ .then(({ data: types }) => Promise.resolve(dispatch(actions.receiveReleaseTypes(types))))
379
+ );
package/selectors.ts ADDED
@@ -0,0 +1,22 @@
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/selectors';
15
+ export * from './commonSelectors';
16
+ export * from './deploymentsSlice/selectors';
17
+ export * from './devicesSlice/selectors';
18
+ export * from './monitorSlice/selectors';
19
+ export * from './onboardingSlice/selectors';
20
+ export * from './organizationSlice/selectors';
21
+ export * from './releasesSlice/selectors';
22
+ export * from './usersSlice/selectors';
package/store.ts ADDED
@@ -0,0 +1,89 @@
1
+ // Copyright 2019 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 { combineReducers, configureStore } from '@reduxjs/toolkit';
16
+
17
+ import actions from './actions';
18
+ import appSlice from './appSlice';
19
+ import { getToken } from './auth';
20
+ import { USER_LOGOUT, settingsKeys } from './constants';
21
+ import deploymentSlice from './deploymentsSlice';
22
+ import deviceSlice from './devicesSlice';
23
+ import monitorSlice from './monitorSlice';
24
+ import onboardingSlice from './onboardingSlice';
25
+ import organizationSlice, { actions as organizationActions } from './organizationSlice';
26
+ import releaseSlice from './releasesSlice';
27
+ import userSlice from './usersSlice';
28
+ import { extractErrorMessage, preformatWithRequestID } from './utils';
29
+
30
+ const { setSnackbar, uploadProgress } = actions;
31
+
32
+ // exclude 'pendings-redirect' since this is expected to persist refreshes - the rest should be better to be redone
33
+ const keys = ['sessionDeploymentChecker', settingsKeys.initialized];
34
+ const resetEnvironment = () => {
35
+ keys.map(key => window.sessionStorage.removeItem(key));
36
+ };
37
+
38
+ resetEnvironment();
39
+
40
+ export const commonErrorFallback = 'Please check your connection.';
41
+ export const commonErrorHandler = (err, errorContext, dispatch, fallback, mightBeAuthRelated = false) => {
42
+ const errMsg = extractErrorMessage(err, fallback);
43
+ if (mightBeAuthRelated || getToken()) {
44
+ dispatch(setSnackbar({ message: preformatWithRequestID(err.response, `${errorContext} ${errMsg}`), action: 'Copy to clipboard' }));
45
+ }
46
+ return Promise.reject(err);
47
+ };
48
+
49
+ const rootReducer = combineReducers({
50
+ app: appSlice,
51
+ devices: deviceSlice,
52
+ deployments: deploymentSlice,
53
+ monitor: monitorSlice,
54
+ onboarding: onboardingSlice,
55
+ organization: organizationSlice,
56
+ releases: releaseSlice,
57
+ users: userSlice
58
+ });
59
+
60
+ export const sessionReducer = (state, action) => {
61
+ if (action.type === USER_LOGOUT) {
62
+ state = undefined;
63
+ }
64
+ return rootReducer(state, action);
65
+ };
66
+
67
+ export const getConfiguredStore = (options = {}) => {
68
+ const { preloadedState = {}, ...config } = options;
69
+ return configureStore({
70
+ ...config,
71
+ preloadedState,
72
+ reducer: sessionReducer,
73
+ middleware: getDefaultMiddleware =>
74
+ getDefaultMiddleware({
75
+ immutableCheck: {
76
+ ignoredPaths: ['app.uploadsById']
77
+ },
78
+ serializableCheck: {
79
+ ignoredActions: [organizationActions.receiveExternalDeviceIntegrations.name, setSnackbar.name, uploadProgress.name],
80
+ ignoredActionPaths: ['uploads', 'snackbar', /payload\..*$/],
81
+ ignoredPaths: ['app.uploadsById', 'app.snackbar', 'organization.externalDeviceIntegrations']
82
+ }
83
+ })
84
+ });
85
+ };
86
+ export const store = getConfiguredStore({
87
+ preloadedState: {}
88
+ });
89
+ export type AppDispatch = typeof store.dispatch;