@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.
- package/.eslintrc.cjs +9 -0
- package/.prettierignore +1 -0
- package/CHANGELOG.md +12 -0
- package/actions.ts +32 -0
- package/api/general-api.test.ts +51 -0
- package/api/general-api.ts +60 -0
- package/api/types/AWSCredentials.ts +16 -0
- package/api/types/Actor.ts +35 -0
- package/api/types/Addon.ts +17 -0
- package/api/types/AddonTenantadm.ts +17 -0
- package/api/types/Alert.ts +34 -0
- package/api/types/AlertDetails.ts +23 -0
- package/api/types/AlertSubject.ts +23 -0
- package/api/types/ApiBurst.ts +21 -0
- package/api/types/ApiBurstTenantadm.ts +21 -0
- package/api/types/ApiLimits.ts +16 -0
- package/api/types/ApiLimitsTenantadm.ts +16 -0
- package/api/types/ApiQuota.ts +17 -0
- package/api/types/ApiQuotaTenantadm.ts +17 -0
- package/api/types/Artifact.ts +48 -0
- package/api/types/ArtifactDeployments.ts +48 -0
- package/api/types/ArtifactInfo.ts +11 -0
- package/api/types/ArtifactInfoDeployments.ts +11 -0
- package/api/types/ArtifactLink.ts +11 -0
- package/api/types/ArtifactTypeInfo.ts +13 -0
- package/api/types/ArtifactTypeInfoDeployments.ts +13 -0
- package/api/types/ArtifactUpdate.ts +10 -0
- package/api/types/ArtifactUploadLink.ts +15 -0
- package/api/types/Attribute.ts +36 -0
- package/api/types/AttributeInventory.ts +32 -0
- package/api/types/AuditLog.ts +26 -0
- package/api/types/AuthSet.ts +33 -0
- package/api/types/AuthSetDeviceauth.ts +37 -0
- package/api/types/AutoAuthRequest.ts +23 -0
- package/api/types/AzureSharedAccessSecret.ts +11 -0
- package/api/types/BillingInfo.ts +13 -0
- package/api/types/BinaryDeltaConfiguration.ts +15 -0
- package/api/types/BinaryDeltaConfigurationDeployments.ts +15 -0
- package/api/types/BinaryDeltaLimits.ts +15 -0
- package/api/types/BinaryDeltaLimitsDeployments.ts +15 -0
- package/api/types/BoundingBox.ts +11 -0
- package/api/types/CancelRequest.ts +10 -0
- package/api/types/CardData.ts +49 -0
- package/api/types/CardSetupData.ts +17 -0
- package/api/types/CheckoutData.ts +13 -0
- package/api/types/Configuration.ts +11 -0
- package/api/types/Count.ts +13 -0
- package/api/types/Credentials.ts +23 -0
- package/api/types/DBusSubsystem.ts +27 -0
- package/api/types/DeltaConfiguration.ts +14 -0
- package/api/types/DeltaConfigurationDeployments.ts +14 -0
- package/api/types/Deployment.ts +95 -0
- package/api/types/DeploymentAggregation.ts +18 -0
- package/api/types/DeploymentAggregationItem.ts +18 -0
- package/api/types/DeploymentAggregationTerm.ts +23 -0
- package/api/types/DeploymentAggregationTerms.ts +18 -0
- package/api/types/DeploymentAttributeProjection.ts +11 -0
- package/api/types/DeploymentAuditlogs.ts +11 -0
- package/api/types/DeploymentDeployments.ts +99 -0
- package/api/types/DeploymentFilterTerm.ts +37 -0
- package/api/types/DeploymentIdentifier.ts +10 -0
- package/api/types/DeploymentPhase.ts +23 -0
- package/api/types/DeploymentReporting.ts +46 -0
- package/api/types/DeploymentSearchTerms.ts +39 -0
- package/api/types/DeploymentSortTerm.ts +25 -0
- package/api/types/DeploymentStatistics.ts +15 -0
- package/api/types/DeploymentStatusStatistics.ts +54 -0
- package/api/types/Device.ts +29 -0
- package/api/types/DeviceAggregation.ts +18 -0
- package/api/types/DeviceAggregationItem.ts +18 -0
- package/api/types/DeviceAggregationTerm.ts +27 -0
- package/api/types/DeviceAggregationTerms.ts +22 -0
- package/api/types/DeviceAttribute.ts +23 -0
- package/api/types/DeviceAttributeProjection.ts +15 -0
- package/api/types/DeviceAuditlogs.ts +17 -0
- package/api/types/DeviceAuthEvent.ts +23 -0
- package/api/types/DeviceConfiguration.ts +16 -0
- package/api/types/DeviceDeployment.ts +11 -0
- package/api/types/DeviceDeployments.ts +31 -0
- package/api/types/DeviceDeviceauth.ts +42 -0
- package/api/types/DeviceFilterAttribute.ts +22 -0
- package/api/types/DeviceFilterTerm.ts +41 -0
- package/api/types/DeviceInventory.ts +19 -0
- package/api/types/DeviceInventoryInventory.ts +19 -0
- package/api/types/DeviceReporting.ts +22 -0
- package/api/types/DeviceSearchTerms.ts +39 -0
- package/api/types/DeviceSortTerm.ts +29 -0
- package/api/types/DeviceState.ts +32 -0
- package/api/types/DeviceStateDeviceconnect.ts +33 -0
- package/api/types/DeviceStateIot_manager.ts +14 -0
- package/api/types/DeviceStatus.ts +19 -0
- package/api/types/DeviceWithImage.ts +80 -0
- package/api/types/DirectUploadMetadata.ts +18 -0
- package/api/types/EmailVerificationCompletion.ts +13 -0
- package/api/types/EmailVerificationRequest.ts +13 -0
- package/api/types/Error.ts +17 -0
- package/api/types/ErrorAuditlogs.ts +13 -0
- package/api/types/ErrorDeployments.ts +17 -0
- package/api/types/ErrorDeviceauth.ts +13 -0
- package/api/types/ErrorDeviceconfig.ts +17 -0
- package/api/types/ErrorDeviceconnect.ts +17 -0
- package/api/types/ErrorDevicemonitor.ts +17 -0
- package/api/types/ErrorExt.ts +18 -0
- package/api/types/ErrorInventory.ts +17 -0
- package/api/types/ErrorIot_manager.ts +17 -0
- package/api/types/ErrorNotFound.ts +17 -0
- package/api/types/ErrorReporting.ts +18 -0
- package/api/types/ErrorTenantadm.ts +13 -0
- package/api/types/ErrorUseradm.ts +17 -0
- package/api/types/Event.ts +48 -0
- package/api/types/ExternalIdentity.ts +17 -0
- package/api/types/Features.ts +17 -0
- package/api/types/FileUpload.ts +23 -0
- package/api/types/Filter.ts +19 -0
- package/api/types/FilterAttribute.ts +21 -0
- package/api/types/FilterDefinition.ts +18 -0
- package/api/types/FilterDeployments.ts +19 -0
- package/api/types/FilterInventory.ts +20 -0
- package/api/types/FilterPredicate.ts +78 -0
- package/api/types/FilterPredicateDeployments.ts +48 -0
- package/api/types/FilterPredicateInventory.ts +76 -0
- package/api/types/GeoBoundingBox.ts +10 -0
- package/api/types/GeoBoundingBoxFilter.ts +6 -0
- package/api/types/GeoDistance.ts +17 -0
- package/api/types/GeoDistanceFilter.ts +10 -0
- package/api/types/GeoPoint.ts +15 -0
- package/api/types/Group.ts +10 -0
- package/api/types/HTTP.ts +19 -0
- package/api/types/IdentityData.ts +25 -0
- package/api/types/InputParameter.ts +8 -0
- package/api/types/Integration.ts +24 -0
- package/api/types/JobStatus.ts +13 -0
- package/api/types/Limit.ts +21 -0
- package/api/types/LimitDeployments.ts +21 -0
- package/api/types/LimitDeviceauth.ts +10 -0
- package/api/types/LimitUseradm.ts +11 -0
- package/api/types/Limits.ts +11 -0
- package/api/types/LineDescriptor.ts +11 -0
- package/api/types/LogSubsystem.ts +23 -0
- package/api/types/LoginOptions.ts +18 -0
- package/api/types/ManagementAPIConfiguration.ts +5 -0
- package/api/types/MenderTypes.ts +183 -0
- package/api/types/MonitorConfiguration.ts +22 -0
- package/api/types/NewConfigurationDeployment.ts +15 -0
- package/api/types/NewConfigurationDeploymentResponse.ts +10 -0
- package/api/types/NewDeployment.ts +42 -0
- package/api/types/NewDeploymentForGroup.ts +33 -0
- package/api/types/NewDeploymentPhase.ts +37 -0
- package/api/types/NewDeploymentPhaseDeployments.ts +20 -0
- package/api/types/NewDeploymentV2.ts +31 -0
- package/api/types/NewTenant.ts +41 -0
- package/api/types/Object.ts +35 -0
- package/api/types/OwnUserUpdate.ts +21 -0
- package/api/types/PasswordResetCompletion.ts +17 -0
- package/api/types/PasswordResetRequest.ts +13 -0
- package/api/types/Permission.ts +21 -0
- package/api/types/PermissionObject.ts +14 -0
- package/api/types/PermissionSet.ts +22 -0
- package/api/types/PermissionSetWithScope.ts +15 -0
- package/api/types/PersonalAccessToken.ts +29 -0
- package/api/types/PersonalAccessTokenRequest.ts +18 -0
- package/api/types/Plan.ts +19 -0
- package/api/types/PlanBindingDetails.ts +13 -0
- package/api/types/PlanChangeRequest.ts +14 -0
- package/api/types/PlanLimits.ts +12 -0
- package/api/types/PreAuthSet.ts +16 -0
- package/api/types/Release.ts +32 -0
- package/api/types/ReleaseDeployments.ts +32 -0
- package/api/types/ReleaseUpdate.ts +13 -0
- package/api/types/Releases.ts +9 -0
- package/api/types/ReleasesDeleteError.ts +21 -0
- package/api/types/ReleasesDeployments.ts +9 -0
- package/api/types/Role.ts +19 -0
- package/api/types/RolePermission.ts +25 -0
- package/api/types/RolePermissionObject.ts +17 -0
- package/api/types/RoleUseradm.ts +19 -0
- package/api/types/SAMLMetadata.ts +21 -0
- package/api/types/Scope.ts +19 -0
- package/api/types/SelectAttribute.ts +17 -0
- package/api/types/ServiceSubsystem.ts +19 -0
- package/api/types/Settings.ts +23 -0
- package/api/types/SortCriteria.ts +30 -0
- package/api/types/SsoObject.ts +21 -0
- package/api/types/Status.ts +17 -0
- package/api/types/StatusDeviceauth.ts +18 -0
- package/api/types/StatusTenantadm.ts +19 -0
- package/api/types/StorageLimit.ts +17 -0
- package/api/types/SubscriptionData.ts +8 -0
- package/api/types/SupportRequest.ts +17 -0
- package/api/types/Tag.ts +27 -0
- package/api/types/Tags.ts +10 -0
- package/api/types/TaskResult.ts +13 -0
- package/api/types/TaskResultCLI.ts +10 -0
- package/api/types/TaskResultHTTPRequest.ts +8 -0
- package/api/types/TaskResultHTTPResponse.ts +10 -0
- package/api/types/Tenant.ts +38 -0
- package/api/types/TenantApiLimits.ts +12 -0
- package/api/types/TenantApiLimitsTenantadm.ts +12 -0
- package/api/types/TenantIdName.ts +14 -0
- package/api/types/TenantIdsData.ts +10 -0
- package/api/types/TenantInfo.ts +14 -0
- package/api/types/TenantShortInfo.ts +21 -0
- package/api/types/TenantTenantadm.ts +38 -0
- package/api/types/TenantsIdName.ts +6 -0
- package/api/types/Update.ts +17 -0
- package/api/types/UpdateDeployments.ts +18 -0
- package/api/types/UpdateFile.ts +13 -0
- package/api/types/UpdateFileDeployments.ts +13 -0
- package/api/types/UpdateTypes.ts +8 -0
- package/api/types/UpgradeCompleteRequest.ts +23 -0
- package/api/types/User.ts +13 -0
- package/api/types/UserAuditlogs.ts +13 -0
- package/api/types/UserNew.ts +26 -0
- package/api/types/UserUpdate.ts +25 -0
- package/api/types/UserUseradm.ts +45 -0
- package/api/types/UserWithTenantInfo.ts +8 -0
- package/api/types/XDeltaArgs.ts +9 -0
- package/api/types/XDeltaArgsDeployments.ts +9 -0
- package/api/types/XDeltaArgsLimits.ts +10 -0
- package/api/types/XDeltaArgsLimitsDeployments.ts +10 -0
- package/api/users-api.ts +34 -0
- package/appSlice/constants.ts +166 -0
- package/appSlice/index.ts +159 -0
- package/appSlice/reducer.test.ts +97 -0
- package/appSlice/selectors.ts +33 -0
- package/appSlice/thunks.test.ts +118 -0
- package/appSlice/thunks.ts +148 -0
- package/auth.test.ts +36 -0
- package/auth.ts +82 -0
- package/commonConstants.tsx +252 -0
- package/commonSelectors.ts +255 -0
- package/constants.ts +22 -0
- package/deploymentsSlice/constants.ts +175 -0
- package/deploymentsSlice/index.ts +145 -0
- package/deploymentsSlice/reducer.test.ts +108 -0
- package/deploymentsSlice/selectors.ts +58 -0
- package/deploymentsSlice/thunks.test.ts +425 -0
- package/deploymentsSlice/thunks.ts +382 -0
- package/devicesSlice/constants.ts +56 -0
- package/devicesSlice/index.ts +207 -0
- package/devicesSlice/reducer.test.ts +194 -0
- package/devicesSlice/selectors.ts +127 -0
- package/devicesSlice/thunks.test.tsx +1245 -0
- package/devicesSlice/thunks.tsx +1098 -0
- package/monitorSlice/constants.ts +17 -0
- package/monitorSlice/index.ts +65 -0
- package/monitorSlice/reducer.test.ts +79 -0
- package/monitorSlice/selectors.ts +15 -0
- package/monitorSlice/thunks.test.ts +112 -0
- package/monitorSlice/thunks.ts +101 -0
- package/onboardingSlice/constants.ts +53 -0
- package/onboardingSlice/index.ts +60 -0
- package/onboardingSlice/reducer.test.ts +45 -0
- package/onboardingSlice/selectors.ts +15 -0
- package/onboardingSlice/thunks.test.ts +235 -0
- package/onboardingSlice/thunks.ts +142 -0
- package/organizationSlice/constants.ts +82 -0
- package/organizationSlice/index.ts +111 -0
- package/organizationSlice/reducer.test.ts +78 -0
- package/organizationSlice/selectors.ts +45 -0
- package/organizationSlice/thunks.test.ts +619 -0
- package/organizationSlice/thunks.ts +383 -0
- package/package.json +38 -0
- package/releasesSlice/constants.ts +18 -0
- package/releasesSlice/index.ts +116 -0
- package/releasesSlice/reducer.test.ts +78 -0
- package/releasesSlice/selectors.ts +33 -0
- package/releasesSlice/thunks.test.ts +389 -0
- package/releasesSlice/thunks.ts +379 -0
- package/selectors.ts +22 -0
- package/store.ts +89 -0
- package/storehooks.test.tsx +386 -0
- package/storehooks.ts +250 -0
- package/thunks.ts +21 -0
- package/tsconfig.json +11 -0
- package/tsup.config.ts +11 -0
- package/usersSlice/constants.ts +374 -0
- package/usersSlice/index.ts +151 -0
- package/usersSlice/reducer.test.ts +141 -0
- package/usersSlice/selectors.ts +60 -0
- package/usersSlice/thunks.test.ts +660 -0
- package/usersSlice/thunks.ts +773 -0
- package/utils.test.ts +187 -0
- package/utils.ts +249 -0
package/utils.test.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
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 { defaultState, token } from '../../../tests/mockData';
|
|
16
|
+
import { DARK_MODE, LIGHT_MODE } from './constants';
|
|
17
|
+
import {
|
|
18
|
+
generateDeploymentGroupDetails,
|
|
19
|
+
groupDeploymentDevicesStats,
|
|
20
|
+
groupDeploymentStats,
|
|
21
|
+
isDarkMode,
|
|
22
|
+
mapDeviceAttributes,
|
|
23
|
+
preformatWithRequestID
|
|
24
|
+
} from './utils';
|
|
25
|
+
|
|
26
|
+
describe('mapDeviceAttributes function', () => {
|
|
27
|
+
const defaultAttributes = {
|
|
28
|
+
inventory: { device_type: [], artifact_name: '' },
|
|
29
|
+
identity: {},
|
|
30
|
+
monitor: {},
|
|
31
|
+
system: {},
|
|
32
|
+
tags: {}
|
|
33
|
+
};
|
|
34
|
+
it('works with empty attributes', async () => {
|
|
35
|
+
expect(mapDeviceAttributes()).toEqual(defaultAttributes);
|
|
36
|
+
expect(mapDeviceAttributes([])).toEqual(defaultAttributes);
|
|
37
|
+
});
|
|
38
|
+
it('handles unscoped attributes', async () => {
|
|
39
|
+
const testAttributesObject1 = { name: 'this1', value: 'that1' };
|
|
40
|
+
expect(mapDeviceAttributes([testAttributesObject1])).toEqual({
|
|
41
|
+
...defaultAttributes,
|
|
42
|
+
inventory: {
|
|
43
|
+
...defaultAttributes.inventory,
|
|
44
|
+
this1: 'that1'
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const testAttributesObject2 = { name: 'this2', value: 'that2' };
|
|
48
|
+
expect(mapDeviceAttributes([testAttributesObject1, testAttributesObject2])).toEqual({
|
|
49
|
+
...defaultAttributes,
|
|
50
|
+
inventory: {
|
|
51
|
+
...defaultAttributes.inventory,
|
|
52
|
+
this1: 'that1',
|
|
53
|
+
this2: 'that2'
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
expect(mapDeviceAttributes([testAttributesObject1, testAttributesObject2, testAttributesObject2])).toEqual({
|
|
57
|
+
...defaultAttributes,
|
|
58
|
+
inventory: {
|
|
59
|
+
...defaultAttributes.inventory,
|
|
60
|
+
this1: 'that1',
|
|
61
|
+
this2: 'that2'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
it('handles scoped attributes', async () => {
|
|
66
|
+
const testAttributesObject1 = { name: 'this1', value: 'that1', scope: 'inventory' };
|
|
67
|
+
expect(mapDeviceAttributes([testAttributesObject1])).toEqual({
|
|
68
|
+
...defaultAttributes,
|
|
69
|
+
inventory: {
|
|
70
|
+
...defaultAttributes.inventory,
|
|
71
|
+
this1: 'that1'
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const testAttributesObject2 = { name: 'this2', value: 'that2', scope: 'identity' };
|
|
75
|
+
expect(mapDeviceAttributes([testAttributesObject1, testAttributesObject2])).toEqual({
|
|
76
|
+
...defaultAttributes,
|
|
77
|
+
identity: {
|
|
78
|
+
...defaultAttributes.identity,
|
|
79
|
+
this2: 'that2'
|
|
80
|
+
},
|
|
81
|
+
inventory: {
|
|
82
|
+
...defaultAttributes.inventory,
|
|
83
|
+
this1: 'that1'
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
expect(mapDeviceAttributes([testAttributesObject1, testAttributesObject2, testAttributesObject2])).toEqual({
|
|
87
|
+
...defaultAttributes,
|
|
88
|
+
identity: {
|
|
89
|
+
...defaultAttributes.identity,
|
|
90
|
+
this2: 'that2'
|
|
91
|
+
},
|
|
92
|
+
inventory: {
|
|
93
|
+
...defaultAttributes.inventory,
|
|
94
|
+
this1: 'that1'
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('preformatWithRequestID function', () => {
|
|
101
|
+
it('works as expected', async () => {
|
|
102
|
+
expect(preformatWithRequestID({ data: { request_id: 'someUuidLikeLongerText' } }, token)).toEqual(
|
|
103
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTNkMGY4Yy1hZWRlLTQwMzAtYjM5MS03ZDUwMjBlYjg3M2UiLCJzdWIiOiJhMzBhNzgwYi1iODQzLTUzNDQtODBlMy0wZmQ5NWE0ZjZmYzMiLCJleHAiOjE2MDY4MTUzNjksImlhdCI6MTYwNjIxMDU2OSwibWVuZGVyLnRlbmF... [Request ID: someUuid]'
|
|
104
|
+
);
|
|
105
|
+
expect(preformatWithRequestID({ data: {} }, token)).toEqual(
|
|
106
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTNkMGY4Yy1hZWRlLTQwMzAtYjM5MS03ZDUwMjBlYjg3M2UiLCJzdWIiOiJhMzBhNzgwYi1iODQzLTUzNDQtODBlMy0wZmQ5NWE0ZjZmYzMiLCJleHAiOjE2MDY4MTUzNjksImlhdCI6MTYwNjIxMDU2OSwibWVuZGVyLnRlbmF...'
|
|
107
|
+
);
|
|
108
|
+
expect(preformatWithRequestID(undefined, token)).toEqual(
|
|
109
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJjZTNkMGY4Yy1hZWRlLTQwMzAtYjM5MS03ZDUwMjBlYjg3M2UiLCJzdWIiOiJhMzBhNzgwYi1iODQzLTUzNDQtODBlMy0wZmQ5NWE0ZjZmYzMiLCJleHAiOjE2MDY4MTUzNjksImlhdCI6MTYwNjIxMDU2OSwibWVuZGVyLnRlbmF...'
|
|
110
|
+
);
|
|
111
|
+
const expectedText = 'short text';
|
|
112
|
+
expect(preformatWithRequestID({ data: { request_id: 'someUuidLikeLongerText' } }, expectedText)).toEqual('short text [Request ID: someUuid]');
|
|
113
|
+
expect(preformatWithRequestID(undefined, expectedText)).toEqual(expectedText);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('generateDeploymentGroupDetails function', () => {
|
|
118
|
+
it('works as expected', async () => {
|
|
119
|
+
expect(generateDeploymentGroupDetails({ terms: defaultState.devices.groups.byId.testGroupDynamic.filters }, 'testGroupDynamic')).toEqual(
|
|
120
|
+
'testGroupDynamic (group = things)'
|
|
121
|
+
);
|
|
122
|
+
expect(
|
|
123
|
+
generateDeploymentGroupDetails(
|
|
124
|
+
{
|
|
125
|
+
terms: [
|
|
126
|
+
{ scope: 'system', key: 'group', operator: '$eq', value: 'things' },
|
|
127
|
+
{ scope: 'system', key: 'group', operator: '$nexists', value: 'otherThings' },
|
|
128
|
+
{ scope: 'system', key: 'group', operator: '$nin', value: 'a,small,list' }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
'testGroupDynamic'
|
|
132
|
+
)
|
|
133
|
+
).toEqual(`testGroupDynamic (group = things, group doesn't exist otherThings, group not in a,small,list)`);
|
|
134
|
+
expect(generateDeploymentGroupDetails({ terms: undefined }, 'testGroupDynamic')).toEqual('testGroupDynamic');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('deployment stats grouping functions', () => {
|
|
139
|
+
it('groups correctly based on deployment stats', async () => {
|
|
140
|
+
let deployment = {
|
|
141
|
+
statistics: {
|
|
142
|
+
status: {
|
|
143
|
+
aborted: 2,
|
|
144
|
+
'already-installed': 1,
|
|
145
|
+
decommissioned: 1,
|
|
146
|
+
downloading: 3,
|
|
147
|
+
failure: 1,
|
|
148
|
+
installing: 1,
|
|
149
|
+
noartifact: 1,
|
|
150
|
+
pending: 2,
|
|
151
|
+
paused: 0,
|
|
152
|
+
rebooting: 1,
|
|
153
|
+
success: 1
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
expect(groupDeploymentStats(deployment)).toEqual({ inprogress: 5, paused: 0, pending: 2, successes: 3, failures: 4 });
|
|
158
|
+
deployment = { ...deployment, max_devices: 100, device_count: 10 };
|
|
159
|
+
expect(groupDeploymentStats(deployment)).toEqual({ inprogress: 5, paused: 0, pending: 92, successes: 3, failures: 4 });
|
|
160
|
+
});
|
|
161
|
+
it('groups correctly based on deployment devices states', async () => {
|
|
162
|
+
const deployment = {
|
|
163
|
+
devices: {
|
|
164
|
+
a: { status: 'aborted' },
|
|
165
|
+
b: { status: 'already-installed' },
|
|
166
|
+
c: { status: 'decommissioned' },
|
|
167
|
+
d: { status: 'downloading' },
|
|
168
|
+
e: { status: 'failure' },
|
|
169
|
+
f: { status: 'installing' },
|
|
170
|
+
g: { status: 'noartifact' },
|
|
171
|
+
h: { status: 'pending' },
|
|
172
|
+
i: { status: 'rebooting' },
|
|
173
|
+
j: { status: 'success' }
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
expect(groupDeploymentDevicesStats(deployment)).toEqual({ inprogress: 3, paused: 0, pending: 1, successes: 3, failures: 3 });
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('isDarkMode function', () => {
|
|
181
|
+
it('should return `true` if DARK_MODE was passed in', () => {
|
|
182
|
+
expect(isDarkMode(DARK_MODE)).toEqual(true);
|
|
183
|
+
});
|
|
184
|
+
it('should return `false` if LIGHT_MODE was passed in', () => {
|
|
185
|
+
expect(isDarkMode(LIGHT_MODE)).toEqual(false);
|
|
186
|
+
});
|
|
187
|
+
});
|
package/utils.ts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
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 { duplicateFilter, yes } from '@northern.tech/utils/helpers';
|
|
16
|
+
|
|
17
|
+
import { ATTRIBUTE_SCOPES, DEVICE_FILTERING_OPTIONS, DEVICE_ISSUE_OPTIONS, DEVICE_LIST_MAXIMUM_LENGTH, emptyUiPermissions } from './commonConstants';
|
|
18
|
+
import {
|
|
19
|
+
DARK_MODE,
|
|
20
|
+
DEPLOYMENT_STATES,
|
|
21
|
+
defaultStats,
|
|
22
|
+
deploymentDisplayStates,
|
|
23
|
+
deploymentStatesToSubstates,
|
|
24
|
+
deploymentStatesToSubstatesWithSkipped,
|
|
25
|
+
emptyFilter
|
|
26
|
+
} from './constants';
|
|
27
|
+
|
|
28
|
+
// for some reason these functions can not be stored in the deviceConstants...
|
|
29
|
+
const filterProcessors = {
|
|
30
|
+
$gt: val => Number(val) || val,
|
|
31
|
+
$gte: val => Number(val) || val,
|
|
32
|
+
$lt: val => Number(val) || val,
|
|
33
|
+
$lte: val => Number(val) || val,
|
|
34
|
+
$in: val => ('' + val).split(',').map(i => i.trim()),
|
|
35
|
+
$nin: val => ('' + val).split(',').map(i => i.trim()),
|
|
36
|
+
$exists: yes,
|
|
37
|
+
$nexists: () => false
|
|
38
|
+
};
|
|
39
|
+
const filterAliases = {
|
|
40
|
+
$nexists: { alias: DEVICE_FILTERING_OPTIONS.$exists.key, value: false }
|
|
41
|
+
};
|
|
42
|
+
export const mapFiltersToTerms = filters =>
|
|
43
|
+
filters.map(filter => ({
|
|
44
|
+
scope: filter.scope,
|
|
45
|
+
attribute: filter.key,
|
|
46
|
+
type: filterAliases[filter.operator]?.alias || filter.operator,
|
|
47
|
+
value: filterProcessors.hasOwnProperty(filter.operator) ? filterProcessors[filter.operator](filter.value) : filter.value
|
|
48
|
+
}));
|
|
49
|
+
export const mapTermsToFilters = terms =>
|
|
50
|
+
terms.map(term => {
|
|
51
|
+
const aliasedFilter = Object.entries(filterAliases).find(
|
|
52
|
+
aliasDefinition => aliasDefinition[1].alias === term.type && aliasDefinition[1].value === term.value
|
|
53
|
+
);
|
|
54
|
+
const operator = aliasedFilter ? aliasedFilter[0] : term.type;
|
|
55
|
+
return { scope: term.scope, key: term.attribute, operator, value: term.value };
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const convertIssueOptionsToFilters = (issuesSelection, filtersState = {}) =>
|
|
59
|
+
issuesSelection.map(item => {
|
|
60
|
+
if (typeof DEVICE_ISSUE_OPTIONS[item].filterRule.value === 'function') {
|
|
61
|
+
return { ...DEVICE_ISSUE_OPTIONS[item].filterRule, value: DEVICE_ISSUE_OPTIONS[item].filterRule.value(filtersState) };
|
|
62
|
+
}
|
|
63
|
+
return DEVICE_ISSUE_OPTIONS[item].filterRule;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const convertDeviceListStateToFilters = ({ filters = [], group, groups = { byId: {} }, offlineThreshold, selectedIssues = [], status }) => {
|
|
67
|
+
let applicableFilters = [...filters];
|
|
68
|
+
if (typeof group === 'string' && !(groups.byId[group]?.filters || applicableFilters).length) {
|
|
69
|
+
applicableFilters.push({ key: 'group', value: group, operator: DEVICE_FILTERING_OPTIONS.$eq.key, scope: 'system' });
|
|
70
|
+
}
|
|
71
|
+
const nonMonitorFilters = applicableFilters.filter(
|
|
72
|
+
filter =>
|
|
73
|
+
!Object.values(DEVICE_ISSUE_OPTIONS).some(
|
|
74
|
+
({ filterRule }) => filter.scope !== 'inventory' && filterRule.scope === filter.scope && filterRule.key === filter.key
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
const deviceIssueFilters = convertIssueOptionsToFilters(selectedIssues, { offlineThreshold });
|
|
78
|
+
applicableFilters = [...nonMonitorFilters, ...deviceIssueFilters];
|
|
79
|
+
const effectiveFilters = status
|
|
80
|
+
? [...applicableFilters, { key: 'status', value: status, operator: DEVICE_FILTERING_OPTIONS.$eq.key, scope: 'identity' }]
|
|
81
|
+
: applicableFilters;
|
|
82
|
+
return { applicableFilters: nonMonitorFilters, filterTerms: mapFiltersToTerms(effectiveFilters) };
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const filterCompare = (filter, item) => Object.keys(emptyFilter).every(key => item[key].toString() === filter[key].toString());
|
|
86
|
+
|
|
87
|
+
export const filtersFilter = (item, index, array) => {
|
|
88
|
+
const firstIndex = array.findIndex(filter => filterCompare(filter, item));
|
|
89
|
+
return firstIndex === index;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const listItemMapper = (byId, ids, { defaultObject = {}, cutOffSize = DEVICE_LIST_MAXIMUM_LENGTH }) => {
|
|
93
|
+
return ids.slice(0, cutOffSize).reduce((accu, id) => {
|
|
94
|
+
if (id && byId[id]) {
|
|
95
|
+
accu.push({ ...defaultObject, ...byId[id] });
|
|
96
|
+
}
|
|
97
|
+
return accu;
|
|
98
|
+
}, []);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const mergePermissions = (existingPermissions = { ...emptyUiPermissions }, addedPermissions) =>
|
|
102
|
+
Object.entries(existingPermissions).reduce(
|
|
103
|
+
(accu, [key, value]) => {
|
|
104
|
+
let values;
|
|
105
|
+
if (!accu[key]) {
|
|
106
|
+
accu[key] = value;
|
|
107
|
+
return accu;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
values = [...value, ...accu[key]].filter(duplicateFilter);
|
|
111
|
+
} else {
|
|
112
|
+
values = mergePermissions(accu[key], { ...value });
|
|
113
|
+
}
|
|
114
|
+
accu[key] = values;
|
|
115
|
+
return accu;
|
|
116
|
+
},
|
|
117
|
+
{ ...addedPermissions }
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
export const mapUserRolesToUiPermissions = (userRoles, roles) =>
|
|
121
|
+
userRoles.reduce(
|
|
122
|
+
(accu, roleId) => {
|
|
123
|
+
if (!(roleId && roles[roleId])) {
|
|
124
|
+
return accu;
|
|
125
|
+
}
|
|
126
|
+
return mergePermissions(accu, roles[roleId].uiPermissions);
|
|
127
|
+
},
|
|
128
|
+
{ ...emptyUiPermissions }
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
export const progress = ({ loaded, total }) => {
|
|
132
|
+
let uploadProgress = (loaded / total) * 100;
|
|
133
|
+
return (uploadProgress = uploadProgress < 50 ? Math.ceil(uploadProgress) : Math.round(uploadProgress));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const extractErrorMessage = (err, fallback = '') =>
|
|
137
|
+
err.response?.data?.error?.message || err.response?.data?.error || err.error || err.message || fallback;
|
|
138
|
+
|
|
139
|
+
export const preformatWithRequestID = (res, failMsg) => {
|
|
140
|
+
// ellipsis line
|
|
141
|
+
if (failMsg.length > 100) failMsg = `${failMsg.substring(0, 220)}...`;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
if (res?.data && Object.keys(res.data).includes('request_id')) {
|
|
145
|
+
let shortRequestUUID = res.data['request_id'].substring(0, 8);
|
|
146
|
+
return `${failMsg} [Request ID: ${shortRequestUUID}]`;
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
console.log('failed to extract request id:', e);
|
|
150
|
+
}
|
|
151
|
+
return failMsg;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const getComparisonCompatibleVersion = version => (isNaN(version.charAt(0)) && version !== 'next' ? 'master' : version);
|
|
155
|
+
|
|
156
|
+
export const stringToBoolean = content => {
|
|
157
|
+
if (!content) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const string = content + '';
|
|
161
|
+
switch (string.trim().toLowerCase()) {
|
|
162
|
+
case 'true':
|
|
163
|
+
case 'yes':
|
|
164
|
+
case '1':
|
|
165
|
+
return true;
|
|
166
|
+
case 'false':
|
|
167
|
+
case 'no':
|
|
168
|
+
case '0':
|
|
169
|
+
case null:
|
|
170
|
+
return false;
|
|
171
|
+
default:
|
|
172
|
+
return Boolean(string);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const groupDeploymentDevicesStats = deployment => {
|
|
177
|
+
const deviceStatCollector = (deploymentStates, devices) =>
|
|
178
|
+
Object.values(devices).reduce((accu, device) => (deploymentStates.includes(device.status) ? accu + 1 : accu), 0);
|
|
179
|
+
|
|
180
|
+
const inprogress = deviceStatCollector(deploymentStatesToSubstates.inprogress, deployment.devices);
|
|
181
|
+
const pending = deviceStatCollector(deploymentStatesToSubstates.pending, deployment.devices);
|
|
182
|
+
const successes = deviceStatCollector(deploymentStatesToSubstates.successes, deployment.devices);
|
|
183
|
+
const failures = deviceStatCollector(deploymentStatesToSubstates.failures, deployment.devices);
|
|
184
|
+
const paused = deviceStatCollector(deploymentStatesToSubstates.paused, deployment.devices);
|
|
185
|
+
return { inprogress, paused, pending, successes, failures };
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const statCollector = (items, statistics) => items.reduce((accu, property) => accu + Number(statistics[property] || 0), 0);
|
|
189
|
+
export const groupDeploymentStats = (deployment, withSkipped) => {
|
|
190
|
+
const { statistics = {} } = deployment;
|
|
191
|
+
const { status = {} } = statistics;
|
|
192
|
+
const stats = { ...defaultStats, ...status };
|
|
193
|
+
let groupStates = deploymentStatesToSubstates;
|
|
194
|
+
let result = {};
|
|
195
|
+
if (withSkipped) {
|
|
196
|
+
groupStates = deploymentStatesToSubstatesWithSkipped;
|
|
197
|
+
result.skipped = statCollector(groupStates.skipped, stats);
|
|
198
|
+
}
|
|
199
|
+
result = {
|
|
200
|
+
...result,
|
|
201
|
+
// don't include 'pending' as inprogress, as all remaining devices will be pending - we don't discriminate based on phase membership
|
|
202
|
+
inprogress: statCollector(groupStates.inprogress, stats),
|
|
203
|
+
pending: (deployment.max_devices ? deployment.max_devices - deployment.device_count : 0) + statCollector(groupStates.pending, stats),
|
|
204
|
+
successes: statCollector(groupStates.successes, stats),
|
|
205
|
+
failures: statCollector(groupStates.failures, stats),
|
|
206
|
+
paused: statCollector(groupStates.paused, stats)
|
|
207
|
+
};
|
|
208
|
+
return result;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const getDeploymentState = deployment => {
|
|
212
|
+
const { status: deploymentStatus = DEPLOYMENT_STATES.pending } = deployment;
|
|
213
|
+
const { inprogress: currentProgressCount, paused } = groupDeploymentStats(deployment);
|
|
214
|
+
|
|
215
|
+
let status = deploymentDisplayStates[deploymentStatus];
|
|
216
|
+
if (deploymentStatus === DEPLOYMENT_STATES.pending && currentProgressCount === 0) {
|
|
217
|
+
status = 'queued';
|
|
218
|
+
} else if (paused > 0) {
|
|
219
|
+
status = deploymentDisplayStates.paused;
|
|
220
|
+
}
|
|
221
|
+
return status;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export const generateDeploymentGroupDetails = (filter, groupName) =>
|
|
225
|
+
filter && filter.terms?.length
|
|
226
|
+
? `${groupName} (${filter.terms
|
|
227
|
+
.map(filter => `${filter.attribute || filter.key} ${DEVICE_FILTERING_OPTIONS[filter.type || filter.operator].shortform} ${filter.value}`)
|
|
228
|
+
.join(', ')})`
|
|
229
|
+
: groupName;
|
|
230
|
+
|
|
231
|
+
export const mapDeviceAttributes = (attributes = []) =>
|
|
232
|
+
attributes.reduce(
|
|
233
|
+
(accu, attribute) => {
|
|
234
|
+
if (!(attribute.value && attribute.name) && attribute.scope === ATTRIBUTE_SCOPES.inventory) {
|
|
235
|
+
return accu;
|
|
236
|
+
}
|
|
237
|
+
accu[attribute.scope || ATTRIBUTE_SCOPES.inventory] = {
|
|
238
|
+
...accu[attribute.scope || ATTRIBUTE_SCOPES.inventory],
|
|
239
|
+
[attribute.name]: attribute.value
|
|
240
|
+
};
|
|
241
|
+
if (attribute.name === 'device_type' && attribute.scope === ATTRIBUTE_SCOPES.inventory) {
|
|
242
|
+
accu.inventory.device_type = [].concat(attribute.value);
|
|
243
|
+
}
|
|
244
|
+
return accu;
|
|
245
|
+
},
|
|
246
|
+
{ inventory: { device_type: [], artifact_name: '' }, identity: {}, monitor: {}, system: {}, tags: {} }
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
export const isDarkMode = mode => mode === DARK_MODE;
|