@rancher/shell 3.0.5-rc.6 → 3.0.5-rc.8

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 (243) hide show
  1. package/assets/brand/classic/metadata.json +3 -0
  2. package/assets/styles/app.scss +1 -0
  3. package/assets/styles/base/_color.scss +16 -0
  4. package/assets/styles/base/_helpers.scss +10 -0
  5. package/assets/styles/base/_variables.scss +18 -12
  6. package/assets/styles/fonts/_icons.scss +1 -32
  7. package/assets/styles/global/_layout.scss +1 -1
  8. package/assets/styles/themes/_dark.scss +262 -258
  9. package/assets/styles/themes/_light.scss +538 -509
  10. package/assets/styles/themes/_modern.scss +914 -0
  11. package/assets/translations/en-us.yaml +110 -29
  12. package/chart/__tests__/S3.test.ts +2 -1
  13. package/cloud-credential/generic.vue +18 -10
  14. package/cloud-credential/harvester.vue +1 -9
  15. package/components/AdvancedSection.vue +8 -0
  16. package/components/ChartReadme.vue +17 -7
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/Drawer/Chrome.vue +0 -1
  19. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +27 -28
  20. package/components/Drawer/ResourceDetailDrawer/composables.ts +4 -24
  21. package/components/Drawer/ResourceDetailDrawer/index.vue +18 -4
  22. package/components/InstallHelmCharts.vue +656 -0
  23. package/components/LazyImage.vue +60 -4
  24. package/components/Loading.vue +1 -1
  25. package/components/LocaleSelector.vue +7 -2
  26. package/components/Markdown.vue +4 -0
  27. package/components/PaginatedResourceTable.vue +46 -1
  28. package/components/PromptRestore.vue +22 -44
  29. package/components/Resource/Detail/Masthead/composable.ts +16 -0
  30. package/components/Resource/Detail/Masthead/index.vue +37 -0
  31. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +10 -2
  32. package/components/Resource/Detail/Metadata/IdentifyingInformation/identifying-fields.ts +26 -7
  33. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +8 -1
  34. package/components/Resource/Detail/Metadata/KeyValue.vue +12 -10
  35. package/components/Resource/Detail/Metadata/Rectangle.vue +3 -1
  36. package/components/Resource/Detail/Metadata/__tests__/composables.test.ts +10 -17
  37. package/components/Resource/Detail/Metadata/composables.ts +9 -7
  38. package/components/Resource/Detail/Metadata/index.vue +17 -2
  39. package/components/Resource/Detail/Page.vue +35 -21
  40. package/components/Resource/Detail/SpacedRow.vue +1 -1
  41. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +8 -9
  42. package/components/Resource/Detail/TitleBar/composables.ts +5 -5
  43. package/components/Resource/Detail/TitleBar/index.vue +12 -3
  44. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  45. package/components/ResourceDetail/index.vue +569 -72
  46. package/components/ResourceList/index.vue +1 -0
  47. package/components/ResourceTable.vue +6 -1
  48. package/components/ResourceYaml.vue +1 -1
  49. package/components/RichTranslation.vue +106 -0
  50. package/components/SlideInPanelManager.vue +13 -10
  51. package/components/SortableTable/index.vue +5 -5
  52. package/components/SortableTable/selection.js +0 -1
  53. package/components/Tabbed/index.vue +35 -4
  54. package/components/__tests__/LazyImage.spec.ts +121 -0
  55. package/components/__tests__/PromptRestore.test.ts +1 -65
  56. package/components/__tests__/RichTranslation.test.ts +115 -0
  57. package/components/fleet/FleetStatus.vue +4 -0
  58. package/components/fleet/dashboard/ResourcePanel.vue +2 -1
  59. package/components/form/ClusterAppearance.vue +5 -0
  60. package/components/form/FileImageSelector.vue +1 -1
  61. package/components/form/Members/ClusterPermissionsEditor.vue +1 -1
  62. package/components/form/NameNsDescription.vue +1 -0
  63. package/components/form/Networking.vue +24 -19
  64. package/components/form/ProjectMemberEditor.vue +1 -1
  65. package/components/form/ResourceLabeledSelect.vue +22 -8
  66. package/components/form/ResourceTabs/index.vue +20 -0
  67. package/components/form/SecretSelector.vue +9 -0
  68. package/components/form/SelectOrCreateAuthSecret.vue +6 -3
  69. package/components/form/__tests__/Networking.test.ts +116 -0
  70. package/components/form/labeled-select-utils/labeled-select-pagination.ts +3 -38
  71. package/components/formatter/FleetApplicationSource.vue +25 -17
  72. package/components/formatter/PodImages.vue +1 -1
  73. package/components/formatter/__tests__/LiveDate.test.ts +10 -2
  74. package/components/google/AccountAccess.vue +44 -46
  75. package/components/nav/Favorite.vue +4 -0
  76. package/components/nav/Group.vue +4 -1
  77. package/components/nav/NotificationCenter/Notification.vue +1 -27
  78. package/components/nav/WindowManager/index.vue +3 -3
  79. package/composables/resources.ts +2 -2
  80. package/config/labels-annotations.js +3 -2
  81. package/config/pagination-table-headers.js +8 -1
  82. package/config/product/explorer.js +27 -2
  83. package/config/product/manager.js +0 -1
  84. package/config/query-params.js +10 -0
  85. package/config/router/routes.js +21 -1
  86. package/config/system-namespaces.js +1 -1
  87. package/config/table-headers.js +30 -1
  88. package/config/types.js +1 -1
  89. package/config/version.js +1 -1
  90. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +11 -0
  91. package/detail/__tests__/workload.test.ts +164 -0
  92. package/detail/configmap.vue +33 -75
  93. package/detail/projectsecret.vue +11 -0
  94. package/detail/provisioning.cattle.io.cluster.vue +351 -369
  95. package/detail/secret.vue +49 -308
  96. package/detail/workload/index.vue +38 -21
  97. package/dialog/InstallExtensionDialog.vue +8 -5
  98. package/dialog/RotateEncryptionKeyDialog.vue +10 -30
  99. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  100. package/edit/auth/ldap/__tests__/config.test.ts +14 -0
  101. package/edit/auth/ldap/config.vue +24 -0
  102. package/edit/compliance.cattle.io.clusterscan.vue +1 -1
  103. package/edit/configmap.vue +4 -1
  104. package/edit/fleet.cattle.io.gitrepo.vue +5 -6
  105. package/edit/fleet.cattle.io.helmop.vue +78 -56
  106. package/edit/logging.banzaicloud.io.output/index.vue +1 -1
  107. package/edit/logging.banzaicloud.io.output/providers/awsElasticsearch.vue +5 -6
  108. package/edit/networking.k8s.io.ingress/Certificate.vue +20 -22
  109. package/edit/networking.k8s.io.ingress/DefaultBackend.vue +8 -3
  110. package/edit/networking.k8s.io.ingress/Rule.vue +2 -5
  111. package/edit/networking.k8s.io.ingress/RulePath.vue +17 -11
  112. package/edit/networking.k8s.io.ingress/__tests__/Certificate.test.ts +165 -0
  113. package/edit/networking.k8s.io.networkpolicy/PolicyRuleTarget.vue +11 -10
  114. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -3
  115. package/edit/networking.k8s.io.networkpolicy/index.vue +17 -17
  116. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +3 -2
  117. package/edit/provisioning.cattle.io.cluster/rke2.vue +123 -61
  118. package/edit/provisioning.cattle.io.cluster/tabs/AgentConfiguration.vue +9 -7
  119. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +22 -13
  120. package/edit/provisioning.cattle.io.cluster/tabs/DirectoryConfig.vue +10 -12
  121. package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +39 -38
  122. package/edit/provisioning.cattle.io.cluster/tabs/etcd/S3Config.vue +41 -19
  123. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +16 -3
  124. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +32 -33
  125. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryMirrors.vue +9 -10
  126. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +1 -3
  127. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/DrainOptions.vue +16 -9
  128. package/edit/secret/basic.vue +1 -0
  129. package/edit/secret/index.vue +126 -15
  130. package/edit/workload/index.vue +5 -14
  131. package/list/projectsecret.vue +345 -0
  132. package/list/provisioning.cattle.io.cluster.vue +1 -69
  133. package/list/secret.vue +109 -0
  134. package/machine-config/__tests__/vmwarevsphere.test.ts +5 -7
  135. package/machine-config/google.vue +9 -1
  136. package/machine-config/vmwarevsphere.vue +7 -17
  137. package/mixins/__tests__/brand.spec.ts +2 -2
  138. package/mixins/chart.js +0 -2
  139. package/mixins/create-edit-view/impl.js +10 -1
  140. package/mixins/resource-fetch-api-pagination.js +11 -12
  141. package/mixins/resource-fetch.js +3 -1
  142. package/models/__tests__/chart.test.ts +111 -80
  143. package/models/__tests__/fleet.cattle.io.helmop.test.ts +224 -0
  144. package/models/__tests__/node.test.ts +7 -63
  145. package/models/catalog.cattle.io.app.js +1 -1
  146. package/models/catalog.cattle.io.operation.js +1 -1
  147. package/models/chart.js +36 -20
  148. package/models/cloudcredential.js +2 -163
  149. package/models/cluster/node.js +7 -7
  150. package/models/cluster.x-k8s.io.machine.js +3 -3
  151. package/models/cluster.x-k8s.io.machinedeployment.js +11 -2
  152. package/models/compliance.cattle.io.clusterscan.js +2 -2
  153. package/models/configmap.js +4 -0
  154. package/models/constraints.gatekeeper.sh.constraint.js +1 -1
  155. package/models/fleet-application.js +0 -17
  156. package/models/fleet.cattle.io.cluster.js +2 -2
  157. package/models/fleet.cattle.io.gitrepo.js +15 -1
  158. package/models/fleet.cattle.io.helmop.js +26 -22
  159. package/models/management.cattle.io.setting.js +4 -0
  160. package/models/persistentvolumeclaim.js +1 -1
  161. package/models/pod.js +2 -2
  162. package/models/provisioning.cattle.io.cluster.js +39 -67
  163. package/models/rke.cattle.io.etcdsnapshot.js +1 -1
  164. package/models/secret.js +161 -2
  165. package/models/storage.k8s.io.storageclass.js +2 -2
  166. package/models/workload.js +3 -3
  167. package/package.json +11 -10
  168. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +1 -0
  169. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +4 -1
  170. package/pages/c/_cluster/apps/charts/__tests__/AppChartCardFooter.spec.js +41 -0
  171. package/pages/c/_cluster/apps/charts/chart.vue +422 -174
  172. package/pages/c/_cluster/apps/charts/index.vue +46 -35
  173. package/pages/c/_cluster/apps/charts/install.vue +1 -1
  174. package/pages/c/_cluster/explorer/projectsecret.vue +24 -0
  175. package/pages/c/_cluster/fleet/__tests__/index.test.ts +608 -314
  176. package/pages/c/_cluster/fleet/index.vue +103 -45
  177. package/pages/c/_cluster/manager/cloudCredential/index.vue +2 -59
  178. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +10 -3
  179. package/pages/c/_cluster/uiplugins/index.vue +36 -25
  180. package/plugins/dashboard-store/__tests__/normalize.test.ts +223 -0
  181. package/plugins/dashboard-store/__tests__/resource-class.test.ts +191 -0
  182. package/plugins/dashboard-store/__tests__/utils/normalize-usecases.ts +1526 -0
  183. package/plugins/dashboard-store/actions.js +42 -22
  184. package/plugins/dashboard-store/normalize.js +29 -17
  185. package/plugins/dashboard-store/resource-class.js +83 -17
  186. package/plugins/steve/__tests__/getters.test.ts +1 -1
  187. package/plugins/steve/__tests__/subscribe.spec.ts +259 -1
  188. package/plugins/steve/getters.js +8 -2
  189. package/plugins/steve/resourceWatcher.js +10 -3
  190. package/plugins/steve/steve-pagination-utils.ts +14 -3
  191. package/plugins/steve/subscribe.js +192 -19
  192. package/plugins/steve/worker/web-worker.advanced.js +2 -0
  193. package/rancher-components/Card/Card.vue +0 -18
  194. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.test.ts +15 -0
  195. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +65 -0
  196. package/rancher-components/Pill/RcStatusBadge/index.ts +2 -0
  197. package/rancher-components/Pill/RcStatusBadge/types.ts +5 -0
  198. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.test.ts +33 -0
  199. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +75 -0
  200. package/rancher-components/Pill/RcStatusIndicator/index.ts +2 -0
  201. package/rancher-components/Pill/RcStatusIndicator/types.ts +7 -0
  202. package/rancher-components/Pill/types.ts +2 -0
  203. package/rancher-components/RcButton/RcButton.vue +1 -1
  204. package/rancher-components/RcDropdown/RcDropdown.test.ts +98 -0
  205. package/rancher-components/RcDropdown/RcDropdown.vue +5 -0
  206. package/rancher-components/RcDropdown/RcDropdownItem.vue +7 -1
  207. package/rancher-components/RcDropdown/RcDropdownItemCheckbox.vue +2 -1
  208. package/rancher-components/RcDropdown/RcDropdownItemSelect.vue +2 -1
  209. package/rancher-components/RcDropdown/useDropdownContext.ts +21 -0
  210. package/rancher-components/RcDropdown/useDropdownItem.ts +30 -1
  211. package/rancher-components/RcItemCard/RcItemCard.test.ts +20 -0
  212. package/rancher-components/RcItemCard/RcItemCard.vue +40 -6
  213. package/store/__tests__/catalog.test.ts +93 -1
  214. package/store/aws.js +19 -8
  215. package/store/catalog.js +8 -3
  216. package/types/kube/kube-api.ts +12 -0
  217. package/types/resources/settings.d.ts +1 -1
  218. package/types/shell/index.d.ts +643 -585
  219. package/types/store/pagination.types.ts +16 -6
  220. package/types/uiplugins.ts +73 -0
  221. package/utils/__tests__/back-off.test.ts +354 -0
  222. package/utils/__tests__/create-yaml.test.ts +235 -0
  223. package/utils/__tests__/kontainer.test.ts +19 -0
  224. package/utils/__tests__/uiplugins.test.ts +84 -0
  225. package/utils/back-off.ts +176 -0
  226. package/utils/create-yaml.js +103 -9
  227. package/utils/dynamic-importer.js +8 -0
  228. package/utils/kontainer.ts +3 -5
  229. package/utils/pagination-utils.ts +18 -0
  230. package/utils/style.ts +3 -0
  231. package/utils/uiplugins.ts +29 -2
  232. package/utils/validators/__tests__/setting.test.js +92 -0
  233. package/utils/validators/formRules/__tests__/index.test.ts +88 -7
  234. package/utils/validators/formRules/index.ts +83 -8
  235. package/utils/validators/setting.js +17 -0
  236. package/cloud-credential/__tests__/harvester.test.ts +0 -18
  237. package/components/ResourceDetail/__tests__/index.test.ts +0 -135
  238. package/components/ResourceDetail/legacy.vue +0 -562
  239. package/components/formatter/CloudCredExpired.vue +0 -69
  240. package/models/etcdbackup.js +0 -45
  241. package/pages/explorer/resource/detail/configmap.vue +0 -42
  242. package/pages/explorer/resource/detail/secret.vue +0 -50
  243. package/utils/aws.js +0 -0
@@ -1,135 +0,0 @@
1
- import { mount } from '@vue/test-utils';
2
- import ResourceDetail from '@shell/components/ResourceDetail/index.vue';
3
- import { _EDIT, _VIEW, LEGACY, MODE } from '@shell/config/query-params';
4
- import * as pageEnabled from '@shell/composables/useIsNewDetailPageEnabled';
5
- import flushPromises from 'flush-promises';
6
- import { computed } from 'vue';
7
-
8
- const mockQuery: any = {};
9
- const mockParams: any = {};
10
-
11
- jest.mock('@shell/components/ResourceDetail/legacy.vue', () => ({
12
- template: '<div class="legacy">Legacy</div>',
13
- name: 'Legacy',
14
- props: ['flexContent', 'componentTestId', 'storeOverride', 'resourceOverride', 'parentRouteOverride', 'errorsMap']
15
- }));
16
-
17
- jest.mock('@shell/components/Loading.vue', () => ({
18
- template: '<div class="loading">Loading</div>',
19
- name: 'Loading'
20
- }));
21
-
22
- jest.mock('@shell/pages/explorer/resource/detail/configmap.vue', () => ({
23
- template: '<div class="configmap">configmap</div>',
24
- name: 'configmap',
25
- }));
26
-
27
- jest.mock('vue-router', () => ({
28
- useRoute: () => ({
29
- query: mockQuery,
30
- params: mockParams
31
- })
32
- }));
33
-
34
- jest.mock('@shell/composables/useIsNewDetailPageEnabled');
35
-
36
- describe('component: ResourceDetail/index', () => {
37
- const resourceName = 'configmap';
38
- const useIsNewDetailPageEnabledSpy = jest.spyOn(pageEnabled, 'useIsNewDetailPageEnabled');
39
-
40
- beforeEach(() => {
41
- jest.clearAllMocks();
42
- });
43
-
44
- it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false', async() => {
45
- mockParams.resource = resourceName;
46
- mockQuery[MODE] = _VIEW;
47
-
48
- useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
49
-
50
- const wrapper = mount(ResourceDetail, { });
51
- const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
52
-
53
- expect(legacyComponent.props('flexContent')).toStrictEqual(false);
54
- expect(legacyComponent.props('componentTestId')).toStrictEqual('resource-details');
55
- expect(legacyComponent.props('storeOverride')).toBeUndefined();
56
- expect(legacyComponent.props('resourceOverride')).toBeUndefined();
57
- expect(legacyComponent.props('parentRouteOverride')).toBeUndefined();
58
- expect(legacyComponent.props('errorsMap')).toBeUndefined();
59
- expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
60
- });
61
-
62
- it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false but resourceName has not been added to our mapping', async() => {
63
- mockParams.resource = 'notMapped';
64
- mockQuery[MODE] = _VIEW;
65
-
66
- useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
67
-
68
- const wrapper = mount(ResourceDetail, {});
69
- const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
70
-
71
- expect(legacyComponent.exists()).toBeTruthy();
72
- expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
73
- });
74
-
75
- it('should render legacy component with default props if useIsNewDetailPageEnabledSpy is false but mode is not VIEW', async() => {
76
- mockParams.resource = resourceName;
77
- mockQuery[MODE] = _EDIT;
78
-
79
- useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => false));
80
-
81
- const wrapper = mount(ResourceDetail, {});
82
- const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
83
-
84
- expect(legacyComponent.exists()).toBeTruthy();
85
- expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
86
- });
87
-
88
- it('should render legacy component while forwarding props', async() => {
89
- mockParams.resource = resourceName;
90
- mockQuery[MODE] = _VIEW;
91
-
92
- const flexContent = true;
93
- const componentTestId = 'componentTestId';
94
- const storeOverride = 'storeOverride';
95
- const resourceOverride = 'resourceOverride';
96
- const parentRouteOverride = 'parentRouteOverride';
97
- const errorsMap = { error: 'test' };
98
-
99
- const wrapper = mount(ResourceDetail, {
100
- props: {
101
- flexContent,
102
- componentTestId,
103
- storeOverride,
104
- resourceOverride,
105
- parentRouteOverride,
106
- errorsMap,
107
- }
108
- });
109
- const legacyComponent = wrapper.findComponent<any>({ name: 'Legacy' });
110
-
111
- expect(legacyComponent.props('flexContent')).toStrictEqual(flexContent);
112
- expect(legacyComponent.props('componentTestId')).toStrictEqual(componentTestId);
113
- expect(legacyComponent.props('storeOverride')).toStrictEqual(storeOverride);
114
- expect(legacyComponent.props('resourceOverride')).toStrictEqual(resourceOverride);
115
- expect(legacyComponent.props('parentRouteOverride')).toStrictEqual(parentRouteOverride);
116
- expect(legacyComponent.props('errorsMap')).toStrictEqual(errorsMap);
117
- });
118
-
119
- it('should render new component if useIsNewDetailPageEnabledSpy is true', async() => {
120
- mockParams.resource = resourceName;
121
- mockParams.id = 'ID';
122
- mockQuery[MODE] = _VIEW;
123
- mockQuery[LEGACY] = 'false';
124
-
125
- useIsNewDetailPageEnabledSpy.mockReturnValue(computed(() => true));
126
-
127
- const wrapper = mount(ResourceDetail);
128
-
129
- await flushPromises();
130
- const configmapComponent = wrapper.findComponent<any>({ name: 'configmap' });
131
-
132
- expect(configmapComponent.exists()).toBeTruthy();
133
- expect(useIsNewDetailPageEnabledSpy).toHaveBeenCalledTimes(1);
134
- });
135
- });
@@ -1,562 +0,0 @@
1
- <script>
2
- import CreateEditView from '@shell/mixins/create-edit-view/impl';
3
- import Loading from '@shell/components/Loading';
4
- import ResourceYaml from '@shell/components/ResourceYaml';
5
- import {
6
- _VIEW, _EDIT, _CLONE, _IMPORT, _STAGE, _CREATE,
7
- AS, _YAML, _DETAIL, _CONFIG, _GRAPH, PREVIEW, MODE,
8
- } from '@shell/config/query-params';
9
- import { SCHEMA } from '@shell/config/types';
10
- import { createYaml } from '@shell/utils/create-yaml';
11
- import Masthead from '@shell/components/ResourceDetail/Masthead';
12
- import DetailTop from '@shell/components/DetailTop';
13
- import { clone, diff } from '@shell/utils/object';
14
- import IconMessage from '@shell/components/IconMessage';
15
- import ForceDirectedTreeChart from '@shell/components/ForceDirectedTreeChart';
16
- import { stringify } from '@shell/utils/error';
17
- import { Banner } from '@components/Banner';
18
-
19
- function modeFor(route) {
20
- if ( route.query?.mode === _IMPORT ) {
21
- return _IMPORT;
22
- }
23
-
24
- if ( route.params?.id ) {
25
- return route.query.mode || _VIEW;
26
- } else {
27
- return _CREATE;
28
- }
29
- }
30
-
31
- async function getYaml(store, model) {
32
- let yaml;
33
- const opt = { headers: { accept: 'application/yaml' } };
34
-
35
- if ( model.hasLink('view') ) {
36
- yaml = (await model.followLink('view', opt)).data;
37
- }
38
-
39
- return model.cleanForDownload(yaml);
40
- }
41
-
42
- export default {
43
- emits: ['input'],
44
-
45
- components: {
46
- Loading,
47
- DetailTop,
48
- ForceDirectedTreeChart,
49
- ResourceYaml,
50
- Masthead,
51
- IconMessage,
52
- Banner
53
- },
54
-
55
- mixins: [CreateEditView],
56
-
57
- props: {
58
- storeOverride: {
59
- type: String,
60
- default: null,
61
- },
62
-
63
- resourceOverride: {
64
- type: String,
65
- default: null,
66
- },
67
-
68
- parentRouteOverride: {
69
- type: String,
70
- default: null,
71
- },
72
-
73
- flexContent: {
74
- type: Boolean,
75
- default: false,
76
- },
77
-
78
- /**
79
- * Inherited global identifier prefix for tests
80
- * Define a term based on the parent component to avoid conflicts on multiple components
81
- */
82
- componentTestid: {
83
- type: String,
84
- default: 'resource-details'
85
- },
86
- errorsMap: {
87
- type: Object,
88
- default: null
89
- },
90
- },
91
-
92
- async fetch() {
93
- const store = this.$store;
94
- const route = this.$route;
95
- const params = route.params;
96
- let resourceType = this.resourceOverride || params.resource;
97
-
98
- const inStore = this.storeOverride || store.getters['currentStore'](resourceType);
99
- const realMode = this.realMode;
100
-
101
- // eslint-disable-next-line prefer-const
102
- let { namespace, id } = params;
103
-
104
- // There are 6 "real" modes that can be put into the query string
105
- // These are mapped down to the 3 regular page "mode"s that create-edit-view components
106
- // know about: view, edit, create (stage, import and clone become "create")
107
- const mode = ([_CLONE, _IMPORT, _STAGE].includes(realMode) ? _CREATE : realMode);
108
-
109
- const getGraphConfig = store.getters['type-map/hasGraph'](resourceType);
110
- const hasGraph = !!getGraphConfig;
111
- const hasCustomDetail = store.getters['type-map/hasCustomDetail'](resourceType, id);
112
- const hasCustomEdit = store.getters['type-map/hasCustomEdit'](resourceType, id);
113
-
114
- const schemas = store.getters[`${ inStore }/all`](SCHEMA);
115
-
116
- // As determines what component will be rendered
117
- const requested = route.query[AS];
118
- let as;
119
- let notFound = false;
120
-
121
- if ( mode === _VIEW && hasCustomDetail && (!requested || requested === _DETAIL) ) {
122
- as = _DETAIL;
123
- } else if ( mode === _VIEW && hasGraph && requested === _GRAPH) {
124
- as = _GRAPH;
125
- } else if ( hasCustomEdit && (!requested || requested === _CONFIG) ) {
126
- as = _CONFIG;
127
- } else {
128
- as = _YAML;
129
- }
130
-
131
- this.as = as;
132
-
133
- const options = store.getters[`type-map/optionsFor`](resourceType);
134
-
135
- this.showMasthead = [_CREATE, _EDIT].includes(mode) ? options.resourceEditMasthead : true;
136
- const canViewYaml = options.canYaml;
137
-
138
- if ( options.resource ) {
139
- resourceType = options.resource;
140
- }
141
-
142
- const schema = store.getters[`${ inStore }/schemaFor`](resourceType);
143
- let model, initialModel, liveModel, yaml;
144
-
145
- if ( realMode === _CREATE || realMode === _IMPORT ) {
146
- if ( !namespace ) {
147
- namespace = store.getters['defaultNamespace'];
148
- }
149
-
150
- const data = { type: resourceType };
151
-
152
- if ( schema?.attributes?.namespaced ) {
153
- data.metadata = { namespace };
154
- }
155
-
156
- liveModel = await store.dispatch(`${ inStore }/create`, data);
157
- initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
158
- model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
159
-
160
- if (model.forceYaml === true) {
161
- as = _YAML;
162
- this.as = as;
163
- }
164
-
165
- if ( as === _YAML ) {
166
- if (schema?.fetchResourceFields) {
167
- // fetch resourceFields for createYaml
168
- await schema.fetchResourceFields();
169
- }
170
-
171
- yaml = createYaml(schemas, resourceType, data);
172
- }
173
- } else {
174
- let fqid = id;
175
-
176
- if ( schema.attributes?.namespaced && namespace ) {
177
- fqid = `${ namespace }/${ fqid }`;
178
- }
179
-
180
- try {
181
- liveModel = await store.dispatch(`${ inStore }/find`, {
182
- type: resourceType,
183
- id: fqid,
184
- opt: { watch: true }
185
- });
186
- } catch (e) {
187
- if (e.status === 404 || e.status === 403) {
188
- store.dispatch('loadingError', new Error(this.t('nav.failWhale.resourceIdNotFound', { resource: resourceType, fqid }, true)));
189
- }
190
- liveModel = {};
191
- notFound = fqid;
192
- }
193
-
194
- try {
195
- if (realMode === _VIEW) {
196
- model = liveModel;
197
- } else {
198
- model = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
199
- }
200
- initialModel = await store.dispatch(`${ inStore }/clone`, { resource: liveModel });
201
-
202
- if ( as === _YAML ) {
203
- yaml = await getYaml(this.$store, liveModel);
204
- }
205
- } catch (e) {
206
- this.errors.push(e);
207
- }
208
- if ( as === _YAML ) {
209
- try {
210
- yaml = await getYaml(this.$store, liveModel);
211
- } catch (e) {
212
- this.errors.push(e);
213
- }
214
- }
215
-
216
- if ( as === _GRAPH ) {
217
- this.chartData = liveModel;
218
- }
219
-
220
- if ( [_CLONE, _IMPORT, _STAGE].includes(realMode) ) {
221
- model.cleanForNew();
222
- yaml = model.cleanYaml(yaml, realMode);
223
- }
224
- }
225
-
226
- // Ensure common properties exists
227
- try {
228
- model = await store.dispatch(`${ inStore }/cleanForDetail`, model);
229
- } catch (e) {
230
- this.errors.push(e);
231
- }
232
-
233
- const out = {
234
- hasGraph,
235
- getGraphConfig,
236
- hasCustomDetail,
237
- hasCustomEdit,
238
- canViewYaml,
239
- resourceType,
240
- as,
241
- yaml,
242
- initialModel,
243
- liveModel,
244
- mode,
245
- value: model,
246
- notFound,
247
- };
248
-
249
- for ( const key in out ) {
250
- this[key] = out[key];
251
- }
252
-
253
- if ( this.mode === _CREATE ) {
254
- this.value.applyDefaults(this, realMode);
255
- }
256
- },
257
- data() {
258
- return {
259
- chartData: null,
260
- resourceSubtype: null,
261
-
262
- // Set by fetch
263
- hasGraph: null,
264
- hasCustomDetail: null,
265
- hasCustomEdit: null,
266
- resourceType: null,
267
- asYaml: null,
268
- yaml: null,
269
- liveModel: null,
270
- initialModel: null,
271
- mode: null,
272
- as: null,
273
- value: null,
274
- model: null,
275
- notFound: null,
276
- canViewYaml: null,
277
- errors: []
278
- };
279
- },
280
-
281
- computed: {
282
- realMode() {
283
- // There are 5 "real" modes that you can start in: view, edit, create, stage, clone
284
- const realMode = modeFor(this.$route);
285
-
286
- return realMode;
287
- },
288
-
289
- isView() {
290
- return this.mode === _VIEW;
291
- },
292
-
293
- isYaml() {
294
- return this.as === _YAML;
295
- },
296
-
297
- isDetail() {
298
- return this.as === _DETAIL;
299
- },
300
-
301
- isGraph() {
302
- return this.as === _GRAPH;
303
- },
304
-
305
- offerPreview() {
306
- return this.as === _YAML && [_EDIT, _CLONE, _IMPORT, _STAGE].includes(this.mode);
307
- },
308
-
309
- showComponent() {
310
- switch ( this.as ) {
311
- case _DETAIL: return this.detailComponent;
312
- case _CONFIG: return this.editComponent;
313
- }
314
-
315
- return null;
316
- },
317
- hasErrors() {
318
- return this.errors?.length && Array.isArray(this.errors);
319
- },
320
- mappedErrors() {
321
- return !this.errors ? {} : this.errorsMap || this.errors.reduce((acc, error) => ({
322
- ...acc,
323
- [error]: {
324
- message: error?.data?.message || error,
325
- icon: null
326
- }
327
- }), {});
328
- },
329
- },
330
-
331
- watch: {
332
- '$route'(current, prev) {
333
- if (current.name !== prev.name) {
334
- return;
335
- }
336
- const neu = clone(current.query);
337
- const old = clone(prev.query);
338
-
339
- delete neu[PREVIEW];
340
- delete old[PREVIEW];
341
-
342
- if ( !this.isView ) {
343
- delete neu[AS];
344
- delete old[AS];
345
- }
346
-
347
- const queryDiff = Object.keys(diff(neu, old));
348
-
349
- if (queryDiff.includes(MODE) || queryDiff.includes(AS)) {
350
- this.$fetch();
351
- }
352
- },
353
-
354
- // Auto refresh YAML when the model changes
355
- async 'value.metadata.resourceVersion'(a, b) {
356
- if ( this.mode === _VIEW && this.as === _YAML && a && b && a !== b) {
357
- this.yaml = await getYaml(this.$store, this.liveModel);
358
- }
359
- }
360
- },
361
-
362
- created() {
363
- this.configureResource();
364
- },
365
-
366
- methods: {
367
- stringify,
368
- setSubtype(subtype) {
369
- this.resourceSubtype = subtype;
370
- },
371
-
372
- keyAction(act) {
373
- const m = this.liveModel;
374
-
375
- if ( m?.[act] ) {
376
- m[act]();
377
- }
378
- },
379
- closeError(index) {
380
- this.errors = this.errors.filter((_, i) => i !== index);
381
- },
382
- onYamlError(err) {
383
- this.errors = [];
384
- const errors = Array.isArray(err) ? err : [err];
385
-
386
- errors.forEach((e) => {
387
- if (this.errors.indexOf(e) === -1) {
388
- this.errors.push(e);
389
- }
390
- });
391
- },
392
- /**
393
- * Initializes the resource components based on the provided user and
394
- * resource override.
395
- *
396
- * Configures the detail and edit components for a resource based on the
397
- * user's ID and the specified resource.
398
- *
399
- * @param {Object} user - The user object containing user-specific
400
- * information.
401
- * @param {string|null} resourceOverride - An optional resource override
402
- * string. If not provided, the method will use the default resource from
403
- * the route parameters or the instance's resourceOverride property.
404
- */
405
- configureResource(userId = '', resourceOverride = null) {
406
- const id = userId || this.$route.params.id;
407
- const resource = resourceOverride || this.resourceOverride || this.$route.params.resource;
408
- const options = this.$store.getters[`type-map/optionsFor`](resource);
409
-
410
- const detailResource = options.resourceDetail || options.resource || resource;
411
- const editResource = options.resourceEdit || options.resource || resource;
412
-
413
- // FIXME: These aren't right... signature is (rawType, subType).. not (rawType, resourceId)
414
- // Remove id? How does subtype get in (cluster/node)
415
- this.detailComponent = this.$store.getters['type-map/importDetail'](detailResource, id);
416
- this.editComponent = this.$store.getters['type-map/importEdit'](editResource, id);
417
- },
418
- /**
419
- * Sets the mode and initializes the resource components.
420
- *
421
- * This method sets the mode of the component and configures the resource
422
- * components based on the provided user and resource.
423
- *
424
- * @param {Object} payload - An object containing the mode, user, and
425
- * resource properties.
426
- * @param {string} payload.mode - The mode to set.
427
- * @param {Object} payload.user - The user object containing user-specific
428
- * information.
429
- * @param {string} payload.resource - The resource string to use for
430
- * initialization.
431
- */
432
- setMode({ mode, userId, resource }) {
433
- this.mode = mode;
434
- this.value.id = userId;
435
- this.configureResource(userId, resource);
436
- }
437
- }
438
- };
439
- </script>
440
-
441
- <template>
442
- <Loading v-if="$fetchState.pending || notFound" />
443
- <div v-else>
444
- <Masthead
445
- v-if="showMasthead"
446
- :resource="resourceType"
447
- :value="liveModel"
448
- :mode="mode"
449
- :real-mode="realMode"
450
- :as="as"
451
- :has-graph="hasGraph"
452
- :has-detail="hasCustomDetail"
453
- :has-edit="hasCustomEdit"
454
- :can-view-yaml="canViewYaml"
455
- :resource-subtype="resourceSubtype"
456
- :parent-route-override="parentRouteOverride"
457
- :store-override="storeOverride"
458
- >
459
- <DetailTop
460
- v-if="isView && isDetail"
461
- :value="liveModel"
462
- />
463
- </Masthead>
464
- <div
465
- v-if="hasErrors"
466
- id="cru-errors"
467
- class="cru__errors"
468
- >
469
- <Banner
470
- v-for="(err, i) in errors"
471
- :key="i"
472
- color="error"
473
- :data-testid="`error-banner${i}`"
474
- :label="stringify(mappedErrors[err].message)"
475
- :icon="mappedErrors[err].icon"
476
- :closable="true"
477
- @close="closeError(i)"
478
- />
479
- </div>
480
-
481
- <ForceDirectedTreeChart
482
- v-if="isGraph"
483
- :data="chartData"
484
- :fdc-config="getGraphConfig"
485
- />
486
-
487
- <ResourceYaml
488
- v-else-if="isYaml"
489
- ref="resourceyaml"
490
- :value="value"
491
- :mode="mode"
492
- :yaml="yaml"
493
- :offer-preview="offerPreview"
494
- :done-route="doneRoute"
495
- :done-override="value ? value.doneOverride : null"
496
- :show-errors="false"
497
- @update:value="$emit('input', $event)"
498
- @error="onYamlError"
499
- />
500
-
501
- <component
502
- :is="showComponent"
503
- v-else
504
- ref="comp"
505
- v-model:value="value"
506
- v-bind="$data"
507
- :done-params="doneParams"
508
- :done-route="doneRoute"
509
- :mode="mode"
510
- :initial-value="initialModel"
511
- :live-value="liveModel"
512
- :real-mode="realMode"
513
- :class="{'flex-content': flexContent}"
514
- @update:value="$emit('input', $event)"
515
- @update:mode="setMode"
516
- @set-subtype="setSubtype"
517
- />
518
-
519
- <button
520
- v-if="isView"
521
- v-shortkey.once="['shift','d']"
522
- :data-testid="componentTestid + '-detail'"
523
- class="hide"
524
- @shortkey="keyAction('goToDetail')"
525
- />
526
- <button
527
- v-if="isView"
528
- v-shortkey.once="['shift','c']"
529
- :data-testid="componentTestid + '-config'"
530
- class="hide"
531
- @shortkey="keyAction('goToViewConfig')"
532
- />
533
- <button
534
- v-if="isView"
535
- v-shortkey.once="['shift','y']"
536
- :data-testid="componentTestid + '-yaml'"
537
- class="hide"
538
- @shortkey="keyAction('goToViewYaml')"
539
- />
540
- <button
541
- v-if="isView"
542
- v-shortkey.once="['shift','e']"
543
- :data-testid="componentTestid + '-edit'"
544
- class="hide"
545
- @shortkey="keyAction('goToEdit')"
546
- />
547
- </div>
548
- </template>
549
-
550
- <style lang='scss' scoped>
551
- .flex-content {
552
- display: flex;
553
- flex-direction: column;
554
- flex-grow: 1;
555
- }
556
- .cru__errors {
557
- position: sticky;
558
- top: 0;
559
- z-index: 1;
560
- background-color: var(--header-bg);
561
- }
562
- </style>