@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.3
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/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe, it, expect, jest, beforeEach, afterEach
|
|
3
|
+
} from '@jest/globals';
|
|
4
|
+
import { ResourcesApiClassImpl } from '../resources-api-class';
|
|
5
|
+
import { Store } from 'vuex';
|
|
6
|
+
|
|
7
|
+
describe.each(['cluster', 'management'] as const)('resourcesApiClassImpl with storeType: %s', (storeType) => {
|
|
8
|
+
let mockStore: Store<any>;
|
|
9
|
+
let resourcesApi: ResourcesApiClassImpl;
|
|
10
|
+
let mockDispatch: jest.Mock;
|
|
11
|
+
let mockSchemaFor: jest.Mock;
|
|
12
|
+
let mockPaginationEnabled: jest.Mock;
|
|
13
|
+
let consoleErrorSpy: any;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
17
|
+
|
|
18
|
+
mockDispatch = jest.fn() as any;
|
|
19
|
+
mockSchemaFor = jest.fn() as any;
|
|
20
|
+
mockPaginationEnabled = jest.fn() as any;
|
|
21
|
+
|
|
22
|
+
mockStore = {
|
|
23
|
+
dispatch: mockDispatch,
|
|
24
|
+
getters: {
|
|
25
|
+
[`${ storeType }/schemaFor`]: mockSchemaFor,
|
|
26
|
+
[`${ storeType }/paginationEnabled`]: mockPaginationEnabled
|
|
27
|
+
}
|
|
28
|
+
} as any;
|
|
29
|
+
|
|
30
|
+
resourcesApi = new ResourcesApiClassImpl(mockStore, storeType);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
consoleErrorSpy.mockRestore();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('find', () => {
|
|
38
|
+
it('should successfully find a namespaced resource with namespace/name id', async() => {
|
|
39
|
+
// Arrange
|
|
40
|
+
const mockResource = {
|
|
41
|
+
metadata: { name: 'test-resource', namespace: 'default' },
|
|
42
|
+
spec: { data: 'value' }
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: true } });
|
|
46
|
+
mockDispatch.mockResolvedValue(mockResource);
|
|
47
|
+
|
|
48
|
+
// Act
|
|
49
|
+
const result = await resourcesApi.find('pod', 'default/test-pod', { watch: true });
|
|
50
|
+
|
|
51
|
+
// Assert
|
|
52
|
+
expect(result).toStrictEqual(mockResource);
|
|
53
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/find`, {
|
|
54
|
+
type: 'pod',
|
|
55
|
+
id: 'default/test-pod',
|
|
56
|
+
opt: { watch: true }
|
|
57
|
+
});
|
|
58
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should successfully find a cluster-scoped resource with plain id', async() => {
|
|
62
|
+
// Arrange
|
|
63
|
+
const mockResource = {
|
|
64
|
+
metadata: { name: 'worker-1' },
|
|
65
|
+
status: { conditions: [] }
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: false } });
|
|
69
|
+
mockDispatch.mockResolvedValue(mockResource);
|
|
70
|
+
|
|
71
|
+
// Act
|
|
72
|
+
const result = await resourcesApi.find('node', 'worker-1');
|
|
73
|
+
|
|
74
|
+
// Assert
|
|
75
|
+
expect(result).toStrictEqual(mockResource);
|
|
76
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/find`, {
|
|
77
|
+
type: 'node',
|
|
78
|
+
id: 'worker-1',
|
|
79
|
+
opt: {}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should throw error when namespaced resource id does not contain namespace', async() => {
|
|
84
|
+
// Arrange
|
|
85
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: true } });
|
|
86
|
+
|
|
87
|
+
// Act & Assert
|
|
88
|
+
await expect(resourcesApi.find('pod', 'test-pod')).rejects.toThrow(
|
|
89
|
+
`Resource API error - ${ storeType } - Resource "pod" is namespaced. The resourceId must be in "namespace/name" format, but received "test-pod"`
|
|
90
|
+
);
|
|
91
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
92
|
+
`Resource API error - ${ storeType } - Resource "pod" is namespaced. The resourceId must be in "namespace/name" format, but received "test-pod"`
|
|
93
|
+
);
|
|
94
|
+
expect(mockDispatch).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should not validate namespace when schema is not found', async() => {
|
|
98
|
+
// Arrange
|
|
99
|
+
const mockResource = { metadata: { name: 'custom-resource' } };
|
|
100
|
+
|
|
101
|
+
mockSchemaFor.mockReturnValue(null);
|
|
102
|
+
mockDispatch.mockResolvedValue(mockResource);
|
|
103
|
+
|
|
104
|
+
// Act
|
|
105
|
+
const result = await resourcesApi.find('custom.crd', 'my-resource');
|
|
106
|
+
|
|
107
|
+
// Assert
|
|
108
|
+
expect(result).toStrictEqual(mockResource);
|
|
109
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/find`, {
|
|
110
|
+
type: 'custom.crd',
|
|
111
|
+
id: 'my-resource',
|
|
112
|
+
opt: {}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should throw error and log when dispatch fails', async() => {
|
|
117
|
+
// Arrange
|
|
118
|
+
const error = new Error('Network error');
|
|
119
|
+
|
|
120
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: true } });
|
|
121
|
+
mockDispatch.mockRejectedValue(error);
|
|
122
|
+
|
|
123
|
+
// Act & Assert
|
|
124
|
+
await expect(resourcesApi.find('pod', 'default/test-pod')).rejects.toThrow(
|
|
125
|
+
`Resource API error - ${ storeType } - Failed to find resource pod/default/test-pod: Network error`
|
|
126
|
+
);
|
|
127
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
128
|
+
`Resource API error - ${ storeType } - Failed to find resource pod/default/test-pod: Network error`
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should return null when dispatch returns undefined', async() => {
|
|
133
|
+
// Arrange
|
|
134
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: true } });
|
|
135
|
+
mockDispatch.mockResolvedValue(undefined);
|
|
136
|
+
|
|
137
|
+
// Act
|
|
138
|
+
const result = await resourcesApi.find('pod', 'default/nonexistent-pod');
|
|
139
|
+
|
|
140
|
+
// Assert
|
|
141
|
+
expect(result).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should handle resources with different types correctly', async() => {
|
|
145
|
+
// Arrange
|
|
146
|
+
const mockDeployment = {
|
|
147
|
+
metadata: { name: 'test-deployment', namespace: 'kube-system' },
|
|
148
|
+
spec: { replicas: 3 }
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
mockSchemaFor.mockReturnValue({ attributes: { namespaced: true } });
|
|
152
|
+
mockDispatch.mockResolvedValue(mockDeployment);
|
|
153
|
+
|
|
154
|
+
// Act
|
|
155
|
+
const result = await resourcesApi.find('apps.deployment', 'kube-system/test-deployment');
|
|
156
|
+
|
|
157
|
+
// Assert
|
|
158
|
+
expect(result).toStrictEqual(mockDeployment);
|
|
159
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/find`, {
|
|
160
|
+
type: 'apps.deployment',
|
|
161
|
+
id: 'kube-system/test-deployment',
|
|
162
|
+
opt: {}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('findFiltered', () => {
|
|
168
|
+
it('should find resources with a label selector', async() => {
|
|
169
|
+
// Arrange
|
|
170
|
+
const mockResources = [
|
|
171
|
+
{ metadata: { name: 'resource-1', labels: { app: 'nginx' } } },
|
|
172
|
+
{ metadata: { name: 'resource-2', labels: { app: 'nginx' } } }
|
|
173
|
+
];
|
|
174
|
+
const labelSelector = { matchLabels: { app: 'nginx' } };
|
|
175
|
+
|
|
176
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
177
|
+
|
|
178
|
+
// Act
|
|
179
|
+
const result = await resourcesApi.findFiltered('pod', { labelSelector });
|
|
180
|
+
|
|
181
|
+
// Assert
|
|
182
|
+
expect(result).toStrictEqual(mockResources);
|
|
183
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findLabelSelector`, {
|
|
184
|
+
type: 'pod',
|
|
185
|
+
matching: {
|
|
186
|
+
namespace: undefined,
|
|
187
|
+
labelSelector
|
|
188
|
+
},
|
|
189
|
+
opt: {}
|
|
190
|
+
});
|
|
191
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should pass namespace to matching when namespaced is provided', async() => {
|
|
195
|
+
// Arrange
|
|
196
|
+
const mockResources = [{ metadata: { name: 'resource-1' } }];
|
|
197
|
+
const labelSelector = { matchLabels: { app: 'nginx' } };
|
|
198
|
+
|
|
199
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
200
|
+
|
|
201
|
+
// Act
|
|
202
|
+
await resourcesApi.findFiltered('pod', { labelSelector, namespaced: 'default' });
|
|
203
|
+
|
|
204
|
+
// Assert
|
|
205
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findLabelSelector`, {
|
|
206
|
+
type: 'pod',
|
|
207
|
+
matching: {
|
|
208
|
+
namespace: 'default',
|
|
209
|
+
labelSelector
|
|
210
|
+
},
|
|
211
|
+
opt: {}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should pass additional options through to opt', async() => {
|
|
216
|
+
// Arrange
|
|
217
|
+
const mockResources = [{ metadata: { name: 'resource-1' } }];
|
|
218
|
+
const labelSelector = { matchLabels: { tier: 'frontend' } };
|
|
219
|
+
|
|
220
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
221
|
+
|
|
222
|
+
// Act
|
|
223
|
+
await resourcesApi.findFiltered('pod', { labelSelector, force: true });
|
|
224
|
+
|
|
225
|
+
// Assert
|
|
226
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findLabelSelector`, {
|
|
227
|
+
type: 'pod',
|
|
228
|
+
matching: {
|
|
229
|
+
namespace: undefined,
|
|
230
|
+
labelSelector
|
|
231
|
+
},
|
|
232
|
+
opt: { force: true }
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should handle matchExpressions in label selector', async() => {
|
|
237
|
+
// Arrange
|
|
238
|
+
const mockResources = [{ metadata: { name: 'resource-1' } }];
|
|
239
|
+
const labelSelector = {
|
|
240
|
+
matchLabels: { app: 'nginx' },
|
|
241
|
+
matchExpressions: [{
|
|
242
|
+
key: 'env', operator: 'In' as const, values: ['prod', 'staging']
|
|
243
|
+
}]
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
247
|
+
|
|
248
|
+
// Act
|
|
249
|
+
await resourcesApi.findFiltered('pod', { labelSelector, namespaced: 'kube-system' });
|
|
250
|
+
|
|
251
|
+
// Assert
|
|
252
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findLabelSelector`, {
|
|
253
|
+
type: 'pod',
|
|
254
|
+
matching: {
|
|
255
|
+
namespace: 'kube-system',
|
|
256
|
+
labelSelector
|
|
257
|
+
},
|
|
258
|
+
opt: {}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should throw error and log when dispatch fails', async() => {
|
|
263
|
+
// Arrange
|
|
264
|
+
const error = new Error('Network error');
|
|
265
|
+
const labelSelector = { matchLabels: { app: 'nginx' } };
|
|
266
|
+
|
|
267
|
+
mockDispatch.mockRejectedValue(error);
|
|
268
|
+
|
|
269
|
+
// Act & Assert
|
|
270
|
+
await expect(resourcesApi.findFiltered('pod', { labelSelector })).rejects.toThrow(
|
|
271
|
+
`Resource API error - ${ storeType } - Failed to find filtered resources pod: Network error`
|
|
272
|
+
);
|
|
273
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
274
|
+
`Resource API error - ${ storeType } - Failed to find filtered resources pod: Network error`
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should handle empty results', async() => {
|
|
279
|
+
// Arrange
|
|
280
|
+
const labelSelector = { matchLabels: { app: 'nonexistent' } };
|
|
281
|
+
|
|
282
|
+
mockDispatch.mockResolvedValue([]);
|
|
283
|
+
|
|
284
|
+
// Act
|
|
285
|
+
const result = await resourcesApi.findFiltered('pod', { labelSelector });
|
|
286
|
+
|
|
287
|
+
// Assert
|
|
288
|
+
expect(result).toStrictEqual([]);
|
|
289
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should return transient response from label selector mode', async() => {
|
|
293
|
+
// Arrange
|
|
294
|
+
const transientResponse = { data: [{ metadata: { name: 'pod-1' } }] };
|
|
295
|
+
const labelSelector = { matchLabels: { app: 'nginx' } };
|
|
296
|
+
|
|
297
|
+
mockDispatch.mockResolvedValue(transientResponse);
|
|
298
|
+
|
|
299
|
+
// Act
|
|
300
|
+
const result = await resourcesApi.findFiltered('pod', {
|
|
301
|
+
labelSelector,
|
|
302
|
+
force: true
|
|
303
|
+
} as any);
|
|
304
|
+
|
|
305
|
+
// Assert
|
|
306
|
+
expect(result).toStrictEqual(transientResponse);
|
|
307
|
+
expect(result).toHaveProperty('data');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should find resources with pagination options when enabled', async() => {
|
|
311
|
+
// Arrange
|
|
312
|
+
const mockResources = [
|
|
313
|
+
{ metadata: { name: 'pod-1', labels: { app: 'nginx' } } },
|
|
314
|
+
{ metadata: { name: 'pod-2', labels: { app: 'nginx' } } }
|
|
315
|
+
];
|
|
316
|
+
const paginationOptions = {
|
|
317
|
+
pagination: {
|
|
318
|
+
page: 1,
|
|
319
|
+
pageSize: 10,
|
|
320
|
+
sort: []
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
mockPaginationEnabled.mockReturnValue(true);
|
|
325
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
326
|
+
|
|
327
|
+
// Act
|
|
328
|
+
const result = await resourcesApi.findFiltered('pod', paginationOptions as any);
|
|
329
|
+
|
|
330
|
+
// Assert
|
|
331
|
+
expect(result).toStrictEqual(mockResources);
|
|
332
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findPage`, {
|
|
333
|
+
type: 'pod',
|
|
334
|
+
opt: paginationOptions
|
|
335
|
+
});
|
|
336
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
337
|
+
expect(mockPaginationEnabled).toHaveBeenCalledWith('pod');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should return transient response from pagination mode', async() => {
|
|
341
|
+
// Arrange
|
|
342
|
+
const transientResponse = {
|
|
343
|
+
data: [{ metadata: { name: 'pod-1' } }],
|
|
344
|
+
pagination: {
|
|
345
|
+
page: 1, pageSize: 10, total: 50
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const paginationOptions = {
|
|
349
|
+
pagination: {
|
|
350
|
+
page: 1,
|
|
351
|
+
pageSize: 10,
|
|
352
|
+
sort: []
|
|
353
|
+
},
|
|
354
|
+
transient: true
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
mockPaginationEnabled.mockReturnValue(true);
|
|
358
|
+
mockDispatch.mockResolvedValue(transientResponse);
|
|
359
|
+
|
|
360
|
+
// Act
|
|
361
|
+
const result = await resourcesApi.findFiltered('pod', paginationOptions as any);
|
|
362
|
+
|
|
363
|
+
// Assert
|
|
364
|
+
expect(result).toStrictEqual(transientResponse);
|
|
365
|
+
expect(result).toHaveProperty('data');
|
|
366
|
+
expect(result).toHaveProperty('pagination');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('should throw error when pagination is not enabled', async() => {
|
|
370
|
+
// Arrange
|
|
371
|
+
const paginationOptions = {
|
|
372
|
+
pagination: {
|
|
373
|
+
page: 1,
|
|
374
|
+
sort: []
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
mockPaginationEnabled.mockReturnValue(false);
|
|
379
|
+
|
|
380
|
+
// Act & Assert
|
|
381
|
+
await expect(resourcesApi.findFiltered('pod', paginationOptions as any)).rejects.toThrow(
|
|
382
|
+
`Resource API error - ${ storeType } - findFiltered requests with FindFilteredPageOptions are only supported when ui-sql-cache is enabled`
|
|
383
|
+
);
|
|
384
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
385
|
+
`Resource API error - ${ storeType } - findFiltered requests with FindFilteredPageOptions are only supported when ui-sql-cache is enabled`
|
|
386
|
+
);
|
|
387
|
+
expect(mockDispatch).not.toHaveBeenCalled();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should throw error when options are neither pagination nor labelSelector', async() => {
|
|
391
|
+
// Arrange
|
|
392
|
+
const invalidOptions = { someUnknownProp: 'value' } as any;
|
|
393
|
+
|
|
394
|
+
// Act & Assert
|
|
395
|
+
await expect(resourcesApi.findFiltered('pod', invalidOptions)).rejects.toThrow(
|
|
396
|
+
/findFiltered request was made with unknown options/
|
|
397
|
+
);
|
|
398
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
399
|
+
expect.stringContaining('findFiltered request was made with unknown options')
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should pass pagination options with additional properties', async() => {
|
|
404
|
+
// Arrange
|
|
405
|
+
const mockResources = [{ metadata: { name: 'pod-1' } }];
|
|
406
|
+
const paginationOptions = {
|
|
407
|
+
pagination: {
|
|
408
|
+
page: 1,
|
|
409
|
+
sort: [],
|
|
410
|
+
filters: []
|
|
411
|
+
},
|
|
412
|
+
watch: true,
|
|
413
|
+
force: true
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
mockPaginationEnabled.mockReturnValue(true);
|
|
417
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
418
|
+
|
|
419
|
+
// Act
|
|
420
|
+
await resourcesApi.findFiltered('pod', paginationOptions as any);
|
|
421
|
+
|
|
422
|
+
// Assert
|
|
423
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findPage`, {
|
|
424
|
+
type: 'pod',
|
|
425
|
+
opt: paginationOptions
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
describe('findAll', () => {
|
|
431
|
+
it('should successfully fetch all resources by type', async() => {
|
|
432
|
+
// Arrange
|
|
433
|
+
const mockResources = [
|
|
434
|
+
{ metadata: { name: 'resource-1' } },
|
|
435
|
+
{ metadata: { name: 'resource-2' } },
|
|
436
|
+
{ metadata: { name: 'resource-3' } }
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
440
|
+
|
|
441
|
+
// Act
|
|
442
|
+
const result = await resourcesApi.findAll('pod');
|
|
443
|
+
|
|
444
|
+
// Assert
|
|
445
|
+
expect(result).toStrictEqual(mockResources);
|
|
446
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findAll`, {
|
|
447
|
+
type: 'pod',
|
|
448
|
+
opt: {}
|
|
449
|
+
});
|
|
450
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should pass empty options when none provided', async() => {
|
|
454
|
+
// Arrange
|
|
455
|
+
const mockResources = [
|
|
456
|
+
{ metadata: { name: 'resource-1' } },
|
|
457
|
+
{ metadata: { name: 'resource-2' } }
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
461
|
+
|
|
462
|
+
// Act
|
|
463
|
+
await resourcesApi.findAll('pod');
|
|
464
|
+
|
|
465
|
+
// Assert
|
|
466
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findAll`, {
|
|
467
|
+
type: 'pod',
|
|
468
|
+
opt: {}
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('should pass namespaced filter correctly', async() => {
|
|
473
|
+
// Arrange
|
|
474
|
+
const mockResources = [{ metadata: { name: 'resource-1', namespace: 'default' } }];
|
|
475
|
+
const options = { namespaced: ['default', 'kube-system'] };
|
|
476
|
+
|
|
477
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
478
|
+
|
|
479
|
+
// Act
|
|
480
|
+
await resourcesApi.findAll('pod', options);
|
|
481
|
+
|
|
482
|
+
// Assert
|
|
483
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findAll`, {
|
|
484
|
+
type: 'pod',
|
|
485
|
+
opt: options
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('should pass watch option correctly', async() => {
|
|
490
|
+
// Arrange
|
|
491
|
+
const mockResources = [{ metadata: { name: 'resource-1' } }];
|
|
492
|
+
const options = { watch: true };
|
|
493
|
+
|
|
494
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
495
|
+
|
|
496
|
+
// Act
|
|
497
|
+
await resourcesApi.findAll('pod', options);
|
|
498
|
+
|
|
499
|
+
// Assert
|
|
500
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findAll`, {
|
|
501
|
+
type: 'pod',
|
|
502
|
+
opt: options
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it('should pass limit option correctly', async() => {
|
|
507
|
+
// Arrange
|
|
508
|
+
const mockResources = [{ metadata: { name: 'resource-1' } }];
|
|
509
|
+
const options = { limit: 100 };
|
|
510
|
+
|
|
511
|
+
mockDispatch.mockResolvedValue(mockResources);
|
|
512
|
+
|
|
513
|
+
// Act
|
|
514
|
+
await resourcesApi.findAll('pod', options);
|
|
515
|
+
|
|
516
|
+
// Assert
|
|
517
|
+
expect(mockDispatch).toHaveBeenCalledWith(`${ storeType }/findAll`, {
|
|
518
|
+
type: 'pod',
|
|
519
|
+
opt: options
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('should throw error and log when dispatch fails', async() => {
|
|
524
|
+
// Arrange
|
|
525
|
+
const error = new Error('Network error');
|
|
526
|
+
|
|
527
|
+
mockDispatch.mockRejectedValue(error);
|
|
528
|
+
|
|
529
|
+
// Act & Assert
|
|
530
|
+
await expect(resourcesApi.findAll('pod')).rejects.toThrow(
|
|
531
|
+
`Resource API error - ${ storeType } - Failed to find all resources pod: Network error`
|
|
532
|
+
);
|
|
533
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
534
|
+
`Resource API error - ${ storeType } - Failed to find all resources pod: Network error`
|
|
535
|
+
);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('should handle empty results', async() => {
|
|
539
|
+
// Arrange
|
|
540
|
+
mockDispatch.mockResolvedValue([]);
|
|
541
|
+
|
|
542
|
+
// Act
|
|
543
|
+
const result = await resourcesApi.findAll('pod');
|
|
544
|
+
|
|
545
|
+
// Assert
|
|
546
|
+
expect(result).toStrictEqual([]);
|
|
547
|
+
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Store } from 'vuex';
|
|
2
|
+
import { ClusterApi, MgmtApi, ResourcesApiProvider } from '@shell/apis/intf/resources';
|
|
3
|
+
import { ResourcesApiClassImpl } from './resources-api-class';
|
|
4
|
+
import { STORE } from '@shell/store/store-types.js';
|
|
5
|
+
|
|
6
|
+
export class ResourcesApiImpl implements ResourcesApiProvider {
|
|
7
|
+
private clusterApi: ClusterApi;
|
|
8
|
+
private mgmtApi: MgmtApi;
|
|
9
|
+
|
|
10
|
+
constructor(store: Store<any>) {
|
|
11
|
+
this.clusterApi = new ResourcesApiClassImpl(store, STORE.CLUSTER);
|
|
12
|
+
this.mgmtApi = new ResourcesApiClassImpl(store, STORE.MANAGEMENT);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get cluster(): ClusterApi {
|
|
16
|
+
return this.clusterApi;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get mgmt(): MgmtApi {
|
|
20
|
+
return this.mgmtApi;
|
|
21
|
+
}
|
|
22
|
+
}
|