@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.
Files changed (272) hide show
  1. package/apis/impl/apis.ts +6 -0
  2. package/apis/index.ts +26 -0
  3. package/apis/intf/resources-api/cluster-api.ts +18 -0
  4. package/apis/intf/resources-api/mgmt-api.ts +15 -0
  5. package/apis/intf/resources-api/resource-base.ts +107 -0
  6. package/apis/intf/resources-api/resource-constants.ts +147 -0
  7. package/apis/intf/resources-api/resources-api.ts +143 -0
  8. package/apis/intf/resources.ts +49 -0
  9. package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
  10. package/apis/intf/shell-api/proxy.ts +216 -0
  11. package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
  12. package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
  13. package/apis/intf/shell.ts +12 -6
  14. package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
  15. package/apis/resources/index.ts +22 -0
  16. package/apis/resources/resources-api-class.ts +187 -0
  17. package/apis/shell/__tests__/proxy.test.ts +369 -0
  18. package/apis/shell/index.ts +8 -1
  19. package/apis/shell/modal.ts +4 -1
  20. package/apis/shell/notifications.ts +9 -6
  21. package/apis/shell/proxy.ts +256 -0
  22. package/apis/shell/slide-in.ts +4 -1
  23. package/apis/vue-shim.d.ts +2 -1
  24. package/assets/data/aws-regions.json +4 -0
  25. package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
  26. package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
  27. package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
  28. package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
  29. package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
  30. package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
  31. package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
  32. package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
  33. package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
  34. package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
  35. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
  36. package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
  37. package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
  38. package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
  39. package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
  40. package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
  41. package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
  42. package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
  43. package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
  44. package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
  45. package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
  46. package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
  47. package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
  48. package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
  49. package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
  50. package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
  51. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
  52. package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
  53. package/assets/styles/base/_variables.scss +2 -0
  54. package/assets/styles/fonts/_fontstack.scss +132 -8
  55. package/assets/translations/en-us.yaml +22 -5
  56. package/chart/monitoring/index.vue +10 -1
  57. package/components/ActionDropdownShell.vue +2 -1
  58. package/components/CruResourceFooter.vue +9 -5
  59. package/components/ExplorerProjectsNamespaces.vue +1 -1
  60. package/components/InstallHelmCharts.vue +2 -2
  61. package/components/LandingPagePreference.vue +14 -5
  62. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
  63. package/components/Resource/Detail/Metadata/index.vue +6 -0
  64. package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
  65. package/components/Resource/Detail/SpacedRow.vue +3 -1
  66. package/components/Resource/Detail/TitleBar/index.vue +10 -11
  67. package/components/ResourceList/Masthead.vue +12 -8
  68. package/components/SelectIconGrid.vue +0 -10
  69. package/components/SingleClusterInfo.vue +1 -0
  70. package/components/SortableTable/__tests__/sorting.test.ts +126 -0
  71. package/components/SortableTable/index.vue +6 -9
  72. package/components/SortableTable/selection.js +23 -5
  73. package/components/SortableTable/sorting.js +6 -3
  74. package/components/Wizard.vue +14 -13
  75. package/components/fleet/FleetBundles.vue +100 -12
  76. package/components/fleet/FleetClusterTargets/index.vue +37 -15
  77. package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
  78. package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
  79. package/components/form/LabeledSelect.vue +20 -3
  80. package/components/form/NameNsDescription.vue +11 -0
  81. package/components/form/Security.vue +6 -2
  82. package/components/form/WorkloadPorts.vue +2 -7
  83. package/components/form/__tests__/Security.test.ts +76 -0
  84. package/components/formatter/Autoscaler.vue +4 -4
  85. package/components/formatter/ClusterKubeVersion.vue +27 -0
  86. package/components/formatter/ClusterLink.vue +1 -7
  87. package/components/formatter/ClusterProvider.vue +6 -10
  88. package/components/formatter/FleetSummaryGraph.vue +0 -3
  89. package/components/formatter/MachineSummaryGraph.vue +1 -1
  90. package/components/formatter/PodsUsage.vue +2 -2
  91. package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
  92. package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
  93. package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
  94. package/components/nav/NamespaceFilter.vue +2 -2
  95. package/components/nav/TopLevelMenu.helper.ts +15 -3
  96. package/components/nav/TopLevelMenu.vue +16 -5
  97. package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
  98. package/components/templates/home.vue +18 -0
  99. package/components/templates/plain.vue +18 -0
  100. package/components/templates/standalone.vue +17 -0
  101. package/composables/useFormValidation.ts +93 -0
  102. package/composables/useVeeValidateField.test.ts +159 -0
  103. package/composables/useVeeValidateField.ts +67 -0
  104. package/config/pagination-table-headers.js +18 -1
  105. package/config/product/manager.js +82 -21
  106. package/config/router/routes.js +6 -0
  107. package/config/table-headers.js +20 -1
  108. package/config/types.js +2 -1
  109. package/core/__tests__/plugin-products.test.ts +904 -20
  110. package/core/plugin-products-base.ts +107 -7
  111. package/core/plugin-products.ts +4 -0
  112. package/core/plugin-types.ts +111 -1
  113. package/core/plugin.ts +15 -7
  114. package/core/productDebugger.js +9 -4
  115. package/core/types-provisioning.ts +43 -30
  116. package/core/types.ts +57 -20
  117. package/detail/__tests__/pod.test.ts +41 -0
  118. package/detail/harvesterhci.io.management.cluster.vue +6 -2
  119. package/detail/pod.vue +1 -1
  120. package/detail/provisioning.cattle.io.cluster.vue +4 -10
  121. package/edit/auth/__tests__/azuread.test.ts +217 -34
  122. package/edit/auth/azuread.vue +122 -14
  123. package/edit/auth/oidc.vue +2 -2
  124. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
  125. package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
  126. package/edit/networking.k8s.io.ingress/index.vue +75 -20
  127. package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
  128. package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
  129. package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
  130. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
  131. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
  132. package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
  133. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
  134. package/edit/secret/__tests__/ssh.test.ts +5 -6
  135. package/edit/secret/basic.vue +31 -0
  136. package/edit/secret/index.vue +68 -17
  137. package/edit/secret/registry.vue +38 -0
  138. package/edit/secret/ssh.vue +29 -0
  139. package/edit/secret/tls.vue +30 -0
  140. package/edit/service.vue +4 -4
  141. package/edit/workload/Upgrading.vue +3 -3
  142. package/edit/workload/__tests__/Upgrading.test.ts +6 -9
  143. package/edit/workload/mixins/workload.js +2 -1
  144. package/list/fleet.cattle.io.bundle.vue +7 -104
  145. package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
  146. package/list/provisioning.cattle.io.cluster.vue +262 -180
  147. package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
  148. package/mixins/__tests__/chart.test.ts +112 -0
  149. package/mixins/brand.js +2 -1
  150. package/mixins/chart.js +12 -8
  151. package/mixins/resource-fetch-api-pagination.js +41 -5
  152. package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
  153. package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
  154. package/models/__tests__/management.cattle.io.node.ts +6 -5
  155. package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
  156. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
  157. package/models/base-cluster.x-k8s.io.js +26 -0
  158. package/models/cluster.js +1 -1
  159. package/models/cluster.x-k8s.io.machine.js +4 -22
  160. package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
  161. package/models/cluster.x-k8s.io.machineset.js +2 -20
  162. package/models/compliance.cattle.io.clusterscan.js +130 -2
  163. package/models/ext.cattle.io.kubeconfig.ts +4 -7
  164. package/models/fleet-application.js +3 -1
  165. package/models/management.cattle.io.cluster.js +417 -40
  166. package/models/management.cattle.io.node.js +6 -4
  167. package/models/management.cattle.io.nodepool.js +1 -1
  168. package/models/networking.k8s.io.ingress.js +12 -4
  169. package/models/provisioning.cattle.io.cluster.js +47 -330
  170. package/models/rke.cattle.io.etcdsnapshot.js +1 -2
  171. package/package.json +11 -29
  172. package/pages/__tests__/readme.test.ts +49 -0
  173. package/pages/auth/setup.vue +2 -3
  174. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
  175. package/pages/c/_cluster/apps/charts/chart.vue +60 -8
  176. package/pages/c/_cluster/apps/charts/install.vue +10 -7
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
  178. package/pages/c/_cluster/explorer/index.vue +5 -49
  179. package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
  180. package/pages/c/_cluster/istio/index.vue +21 -6
  181. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
  182. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
  183. package/pages/c/_cluster/uiplugins/index.vue +203 -197
  184. package/pages/diagnostic.vue +13 -17
  185. package/pages/fail-whale.vue +18 -0
  186. package/pages/home.vue +77 -260
  187. package/pages/readme.vue +88 -0
  188. package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
  189. package/plugins/dashboard-store/actions.js +40 -18
  190. package/plugins/dashboard-store/resource-class.js +5 -2
  191. package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
  192. package/plugins/steve/steve-pagination-utils.ts +11 -3
  193. package/plugins/steve/subscribe.js +35 -5
  194. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
  195. package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
  196. package/rancher-components/RcButton/RcButton.test.ts +37 -1
  197. package/rancher-components/RcButton/RcButton.vue +38 -8
  198. package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
  199. package/store/__tests__/catalog.test.ts +115 -1
  200. package/store/__tests__/type-map.test.ts +556 -1
  201. package/store/action-menu.js +8 -3
  202. package/store/auth.js +1 -1
  203. package/store/aws.js +27 -16
  204. package/store/catalog.js +27 -3
  205. package/store/digitalocean.js +20 -38
  206. package/store/index.js +2 -0
  207. package/store/linode.js +25 -40
  208. package/store/pnap.js +1 -0
  209. package/store/type-map.js +111 -29
  210. package/tsconfig.paths.json +8 -8
  211. package/types/kube/kube-api.ts +14 -1
  212. package/types/rancher/steve.api.ts +12 -12
  213. package/types/resources/settings.d.ts +2 -1
  214. package/types/shell/index.d.ts +102 -2
  215. package/types/store/dashboard-store.types.ts +108 -11
  216. package/types/store/pagination.types.ts +6 -3
  217. package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
  218. package/utils/__tests__/async.test.ts +87 -0
  219. package/utils/__tests__/aws.test.ts +140 -0
  220. package/utils/__tests__/banners.test.ts +176 -0
  221. package/utils/__tests__/chart.test.ts +64 -1
  222. package/utils/__tests__/color.test.ts +226 -0
  223. package/utils/__tests__/duration.test.ts +140 -0
  224. package/utils/__tests__/fleet.test.ts +340 -0
  225. package/utils/__tests__/ingress.test.ts +553 -0
  226. package/utils/__tests__/kube.test.ts +68 -0
  227. package/utils/__tests__/namespace-filter.test.ts +109 -0
  228. package/utils/__tests__/pagination-utils.test.ts +361 -0
  229. package/utils/__tests__/parse-externalid.test.ts +137 -0
  230. package/utils/__tests__/perf-setting.utils.test.ts +98 -0
  231. package/utils/__tests__/poller-sequential.test.ts +177 -0
  232. package/utils/__tests__/poller.test.ts +170 -0
  233. package/utils/__tests__/promise.test.ts +346 -0
  234. package/utils/__tests__/settings.test.ts +140 -0
  235. package/utils/__tests__/sort-utils.test.ts +301 -0
  236. package/utils/__tests__/string-utils.test.ts +798 -0
  237. package/utils/__tests__/string.test.ts +23 -1
  238. package/utils/__tests__/style.test.ts +154 -0
  239. package/utils/__tests__/svg-filter.test.ts +184 -0
  240. package/utils/__tests__/units.test.ts +417 -0
  241. package/utils/__tests__/versions.test.ts +128 -0
  242. package/utils/__tests__/xccdf.test.ts +391 -0
  243. package/utils/chart.js +36 -0
  244. package/utils/fleet.ts +13 -3
  245. package/utils/gatekeeper/__tests__/util.test.ts +174 -0
  246. package/utils/gc/__tests__/gc-interval.test.ts +119 -0
  247. package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
  248. package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
  249. package/utils/gc/__tests__/gc.test.ts +487 -0
  250. package/utils/ingress.ts +9 -1
  251. package/utils/pagination-utils.ts +2 -1
  252. package/utils/string.js +25 -2
  253. package/utils/uiplugins.ts +5 -5
  254. package/utils/validators/__tests__/cluster-name.test.ts +110 -0
  255. package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
  256. package/utils/validators/__tests__/index.test.ts +481 -0
  257. package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
  258. package/utils/validators/__tests__/misc-validators.test.ts +246 -0
  259. package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
  260. package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
  261. package/utils/validators/__tests__/role-template.test.ts +149 -0
  262. package/utils/validators/__tests__/service.test.ts +283 -0
  263. package/utils/validators/__tests__/setting.test.js +32 -0
  264. package/utils/validators/formRules/__tests__/index.test.ts +50 -0
  265. package/utils/validators/formRules/index.ts +5 -5
  266. package/utils/validators/machine-pool.ts +1 -1
  267. package/utils/validators/setting.js +18 -3
  268. package/utils/xccdf.ts +418 -0
  269. package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
  270. package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
  271. package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
  272. 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
+ }