@rancher/shell 3.0.8 → 3.0.9-rc.2

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 (192) hide show
  1. package/apis/intf/modal.ts +38 -0
  2. package/apis/intf/slide-in.ts +3 -1
  3. package/apis/shell/__tests__/slide-in.test.ts +36 -0
  4. package/apis/shell/slide-in.ts +5 -1
  5. package/assets/styles/base/_color.scss +1 -0
  6. package/assets/styles/base/_typography.scss +14 -5
  7. package/assets/styles/themes/_light.scss +1 -1
  8. package/assets/styles/themes/_modern.scss +1 -1
  9. package/assets/translations/en-us.yaml +94 -33
  10. package/assets/translations/zh-hans.yaml +0 -2
  11. package/components/ActionMenuShell.vue +4 -4
  12. package/components/CodeMirror.vue +4 -3
  13. package/components/DetailText.vue +54 -7
  14. package/components/Drawer/Chrome.vue +11 -4
  15. package/components/Drawer/DrawerCard.vue +19 -0
  16. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +3 -11
  17. package/components/Drawer/ResourceDetailDrawer/__tests__/ConfigTab.test.ts +2 -2
  18. package/components/Drawer/ResourceDetailDrawer/index.vue +3 -20
  19. package/components/Drawer/types.ts +1 -0
  20. package/components/DynamicContent/DynamicContentCloseButton.vue +2 -2
  21. package/components/LocaleSelector.vue +1 -1
  22. package/components/Markdown.vue +1 -1
  23. package/components/PopoverCard.vue +3 -3
  24. package/components/Resource/Detail/Card/ExtrasCard.vue +39 -0
  25. package/components/Resource/Detail/Card/StateCard/__tests__/composables.test.ts +142 -0
  26. package/components/Resource/Detail/Card/StateCard/composables.ts +41 -11
  27. package/components/Resource/Detail/Card/StateCard/index.vue +3 -9
  28. package/components/Resource/Detail/Card/StateCard/types.ts +6 -0
  29. package/components/Resource/Detail/Card/{PodsCard → StatusCard}/index.vue +11 -10
  30. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +24 -25
  31. package/components/Resource/Detail/Cards.vue +27 -0
  32. package/components/Resource/Detail/Masthead/__tests__/index.test.ts +70 -0
  33. package/components/Resource/Detail/Masthead/index.vue +5 -0
  34. package/components/Resource/Detail/Metadata/KeyValueRow.vue +4 -2
  35. package/components/Resource/Detail/ResourcePopover/ResourcePopoverCard.vue +2 -2
  36. package/components/Resource/Detail/ResourceRow.types.ts +14 -0
  37. package/components/Resource/Detail/ResourceRow.vue +23 -35
  38. package/components/Resource/Detail/StatusRow.vue +5 -2
  39. package/components/Resource/Detail/TitleBar/__tests__/composables.test.ts +38 -7
  40. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +106 -2
  41. package/components/Resource/Detail/TitleBar/composables.ts +2 -1
  42. package/components/Resource/Detail/TitleBar/index.vue +41 -6
  43. package/components/ResourceDetail/Masthead/__tests__/index.test.ts +49 -1
  44. package/components/ResourceDetail/Masthead/__tests__/latest.test.ts +85 -0
  45. package/components/ResourceDetail/Masthead/index.vue +1 -0
  46. package/components/ResourceDetail/Masthead/latest.vue +8 -1
  47. package/components/ResourceDetail/Masthead/legacy.vue +1 -1
  48. package/components/Setting.vue +1 -1
  49. package/components/SortableTable/index.vue +25 -0
  50. package/components/SortableTable/selection.js +25 -12
  51. package/components/SortableTable/sorting.js +1 -1
  52. package/components/Tabbed/Tab.vue +1 -0
  53. package/components/Tabbed/index.vue +29 -6
  54. package/components/Window/ContainerShell.vue +10 -13
  55. package/components/fleet/FleetClusterTargets/TargetsList.vue +47 -29
  56. package/components/fleet/FleetClusterTargets/index.vue +82 -29
  57. package/components/fleet/FleetClusters.vue +26 -12
  58. package/components/fleet/FleetGitRepoPaths.vue +2 -2
  59. package/components/fleet/FleetResources.vue +14 -0
  60. package/components/fleet/FleetValuesFrom.vue +2 -2
  61. package/components/fleet/__tests__/FleetClusterTargets.test.ts +531 -0
  62. package/components/fleet/__tests__/FleetClusters.test.ts +576 -0
  63. package/components/fleet/dashboard/ResourceDetails.vue +96 -123
  64. package/components/form/Conditions.vue +1 -15
  65. package/components/form/HookOption.vue +5 -0
  66. package/components/form/LabeledSelect.vue +1 -1
  67. package/components/form/LifecycleHooks.vue +2 -6
  68. package/components/form/ResourceLabeledSelect.vue +12 -1
  69. package/components/form/SeccompProfile.vue +113 -0
  70. package/components/form/Security.vue +244 -133
  71. package/components/form/__tests__/LabeledSelect.test.ts +1 -1
  72. package/components/form/__tests__/SeccompProfile.test.js +124 -0
  73. package/components/form/__tests__/Security.test.ts +125 -37
  74. package/components/formatter/Autoscaler.vue +2 -2
  75. package/components/formatter/FleetSummaryGraph.vue +4 -1
  76. package/components/nav/Group.vue +5 -0
  77. package/components/nav/Header.vue +3 -3
  78. package/components/nav/HeaderPageActionMenu.vue +1 -1
  79. package/components/nav/NamespaceFilter.vue +6 -6
  80. package/components/nav/NotificationCenter/index.vue +1 -1
  81. package/components/nav/TopLevelMenu.helper.ts +41 -16
  82. package/components/nav/TopLevelMenu.vue +45 -25
  83. package/components/nav/WorkspaceSwitcher.vue +1 -1
  84. package/components/nav/__tests__/TopLevelMenu.helper.test.ts +277 -0
  85. package/components/nav/__tests__/TopLevelMenu.test.ts +160 -4
  86. package/components/templates/default.vue +0 -3
  87. package/components/templates/home.vue +0 -3
  88. package/components/templates/plain.vue +0 -3
  89. package/composables/useClickOutside.ts +1 -1
  90. package/config/product/explorer.js +1 -2
  91. package/config/types.js +41 -8
  92. package/detail/__tests__/workload.test.ts +8 -16
  93. package/detail/catalog.cattle.io.app.vue +6 -0
  94. package/detail/fleet.cattle.io.cluster.vue +6 -0
  95. package/detail/workload/index.vue +7 -109
  96. package/edit/__tests__/projectsecret.test.ts +42 -0
  97. package/edit/auth/__tests__/oidc.test.ts +50 -0
  98. package/edit/auth/oidc.vue +68 -44
  99. package/edit/autoscaling.horizontalpodautoscaler/index.vue +140 -59
  100. package/edit/autoscaling.horizontalpodautoscaler/metrics-row.vue +41 -5
  101. package/edit/projectsecret.vue +29 -0
  102. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +89 -200
  103. package/edit/provisioning.cattle.io.cluster/__tests__/Networking.test.ts +58 -17
  104. package/edit/provisioning.cattle.io.cluster/rke2.vue +11 -0
  105. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +3 -63
  106. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +82 -14
  107. package/edit/workload/__tests__/index.test.ts +122 -85
  108. package/edit/workload/index.vue +48 -29
  109. package/edit/workload/mixins/workload.js +85 -32
  110. package/list/catalog.cattle.io.clusterrepo.vue +1 -1
  111. package/list/projectsecret.vue +2 -2
  112. package/machine-config/__tests__/vmwarevsphere.test.ts +64 -0
  113. package/machine-config/amazonec2.vue +2 -2
  114. package/machine-config/vmwarevsphere.vue +58 -4
  115. package/mixins/__tests__/brand.spec.ts +18 -13
  116. package/mixins/__tests__/chart.test.ts +63 -0
  117. package/mixins/chart.js +56 -51
  118. package/models/__tests__/catalog.cattle.io.app.test.ts +33 -0
  119. package/models/__tests__/workload.test.ts +333 -0
  120. package/models/catalog.cattle.io.app.js +8 -0
  121. package/models/pod.js +14 -0
  122. package/models/secret.js +1 -1
  123. package/models/workload.js +93 -27
  124. package/package.json +4 -4
  125. package/pages/c/_cluster/apps/charts/__tests__/install.test.ts +91 -0
  126. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  127. package/pages/c/_cluster/explorer/EventsTable.vue +2 -2
  128. package/pages/c/_cluster/fleet/index.vue +18 -12
  129. package/pages/c/_cluster/manager/hostedprovider/index.vue +1 -19
  130. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -1
  131. package/pages/c/_cluster/uiplugins/index.vue +1 -1
  132. package/plugins/dashboard-store/__tests__/resource-class.test.ts +234 -0
  133. package/plugins/dashboard-store/actions.js +9 -8
  134. package/plugins/dashboard-store/resource-class.js +97 -1
  135. package/plugins/steve/__tests__/revision.test.ts +84 -0
  136. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +30 -0
  137. package/plugins/steve/__tests__/subscribe.spec.ts +134 -0
  138. package/plugins/steve/mutations.js +9 -0
  139. package/plugins/steve/revision.ts +26 -0
  140. package/plugins/steve/steve-pagination-utils.ts +6 -5
  141. package/plugins/steve/subscribe.js +211 -51
  142. package/plugins/subscribe-events.ts +2 -2
  143. package/rancher-components/Form/Checkbox/Checkbox.vue +13 -0
  144. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +1 -1
  145. package/rancher-components/Pill/RcCounterBadge/RcCounterBadge.vue +1 -1
  146. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +3 -1
  147. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +3 -1
  148. package/rancher-components/Pill/RcTag/RcTag.vue +1 -1
  149. package/rancher-components/Pill/index.ts +4 -0
  150. package/rancher-components/RcButton/RcButton.test.ts +53 -9
  151. package/rancher-components/RcButton/RcButton.vue +217 -25
  152. package/rancher-components/RcButton/types.ts +27 -1
  153. package/rancher-components/RcDropdown/RcDropdownMenu.vue +4 -4
  154. package/rancher-components/RcDropdown/types.ts +3 -3
  155. package/rancher-components/RcIcon/RcIcon.test.ts +42 -0
  156. package/rancher-components/RcIcon/RcIcon.vue +9 -6
  157. package/rancher-components/RcIcon/types.ts +13 -9
  158. package/rancher-components/utils/status.test.ts +10 -15
  159. package/rancher-components/utils/status.ts +5 -6
  160. package/store/aws.js +18 -12
  161. package/store/index.js +4 -8
  162. package/store/type-map.utils.ts +1 -1
  163. package/types/kube/kube-api.ts +29 -3
  164. package/types/rancher/steve.api.ts +40 -0
  165. package/types/shell/index.d.ts +99 -0
  166. package/types/store/dashboard-store.types.ts +29 -7
  167. package/types/store/pagination.types.ts +1 -0
  168. package/types/store/subscribe-events.types.ts +1 -0
  169. package/utils/__tests__/azure.test.ts +56 -0
  170. package/utils/__tests__/back-off.test.ts +364 -245
  171. package/utils/__tests__/error.test.ts +44 -0
  172. package/utils/__tests__/fleet.test.ts +8 -1
  173. package/utils/__tests__/pagination-wrapper.test.ts +167 -0
  174. package/utils/__tests__/version.test.ts +55 -1
  175. package/utils/azure.js +12 -0
  176. package/utils/back-off.ts +302 -69
  177. package/utils/cspAdaptor.ts +32 -14
  178. package/utils/dynamic-content/__tests__/index.test.ts +1 -1
  179. package/utils/dynamic-content/__tests__/new-release.test.ts +48 -7
  180. package/utils/dynamic-content/__tests__/support-notice.test.ts +1 -4
  181. package/utils/dynamic-content/index.ts +1 -6
  182. package/utils/dynamic-content/new-release.ts +5 -3
  183. package/utils/dynamic-content/types.d.ts +0 -1
  184. package/utils/error.js +9 -0
  185. package/utils/fleet.ts +2 -2
  186. package/utils/inactivity.ts +2 -3
  187. package/utils/pagination-wrapper.ts +101 -17
  188. package/utils/validators/formRules/index.ts +3 -0
  189. package/utils/version.js +38 -0
  190. package/components/auth/AzureWarning.vue +0 -77
  191. /package/components/Resource/Detail/{Card/PodsCard/Bubble.vue → Bubble.vue} +0 -0
  192. /package/components/Resource/Detail/Card/{PodsCard → StatusCard}/composable.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.8",
3
+ "version": "3.0.9-rc.2",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancher/dashboard",
6
6
  "license": "Apache-2.0",
@@ -67,7 +67,7 @@
67
67
  "cookie": "0.7.0",
68
68
  "core-js": "3.45.0",
69
69
  "cron-validator": "1.4.0",
70
- "cronstrue": "2.53.0",
70
+ "cronstrue": "3.9.0",
71
71
  "cross-env": "7.0.3",
72
72
  "css-loader": "6.7.3",
73
73
  "csv-loader": "3.0.3",
@@ -76,7 +76,7 @@
76
76
  "d3-selection": "3.0.0",
77
77
  "d3": "7.3.0",
78
78
  "dayjs": "1.11.18",
79
- "defu": "5.0.1",
79
+ "defu": "6.1.4",
80
80
  "diff2html": "3.4.24",
81
81
  "dompurify": "3.2.5",
82
82
  "element-matches": "^0.1.2",
@@ -111,7 +111,7 @@
111
111
  "jsonpath-plus": "10.3.0",
112
112
  "jsrsasign": "11.0.0",
113
113
  "jszip": "3.10.1",
114
- "lodash": "4.17.21",
114
+ "lodash": "4.17.23",
115
115
  "marked": "4.0.17",
116
116
  "node-polyfill-webpack-plugin": "3.0.0",
117
117
  "nodemon": "2.0.22",
@@ -0,0 +1,91 @@
1
+
2
+ import { mount } from '@vue/test-utils';
3
+ import Install from '@shell/pages/c/_cluster/apps/charts/install.vue';
4
+ import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
5
+
6
+ describe('page: Install', () => {
7
+ it('should use version annotations for target namespace and name', async() => {
8
+ const mockStore = {
9
+ dispatch: jest.fn((action) => {
10
+ if (action === 'cluster/create') {
11
+ return Promise.resolve({ metadata: { namespace: '', name: '' } });
12
+ }
13
+
14
+ return Promise.resolve();
15
+ }),
16
+ getters: {
17
+ 'catalog/inStore': 'cluster',
18
+ 'features/get': () => false,
19
+ defaultNamespace: 'default',
20
+ 'i18n/withFallback': (key: string) => key,
21
+ 'type-map/hasCustomChart': () => false,
22
+ 'cluster/all': () => [],
23
+ 'cluster/byId': () => null,
24
+ 'management/all': () => [],
25
+ 'prefs/get': () => {},
26
+ 'catalog/charts': [],
27
+ 'wm/byId': () => null,
28
+ 'i18n/t': (key: string) => key,
29
+ }
30
+ };
31
+
32
+ const wrapper = mount(Install, {
33
+ global: {
34
+ mocks: {
35
+ $store: mockStore,
36
+ $route: { query: {} },
37
+ $fetchState: { pending: false },
38
+ t: (key: string) => key,
39
+ },
40
+ stubs: {
41
+ Loading: true,
42
+ Wizard: true,
43
+ Banner: true,
44
+ Checkbox: true,
45
+ LabeledInput: true,
46
+ LabeledSelect: true,
47
+ NameNsDescription: true,
48
+ Tabbed: true,
49
+ Questions: true,
50
+ YamlEditor: true,
51
+ ResourceCancelModal: true,
52
+ UnitInput: true,
53
+ TypeDescription: true,
54
+ LazyImage: true,
55
+ ChartReadme: true,
56
+ ButtonGroup: true,
57
+ }
58
+ },
59
+ data() {
60
+ return {
61
+ version: {
62
+ annotations: {
63
+ [CATALOG_ANNOTATIONS.NAMESPACE]: 'custom-ns',
64
+ [CATALOG_ANNOTATIONS.RELEASE_NAME]: 'custom-name',
65
+ }
66
+ },
67
+ chart: {
68
+ targetNamespace: 'wrong-ns',
69
+ targetName: 'wrong-name',
70
+ versions: []
71
+ },
72
+ query: { versionName: '1.0.0' }
73
+ };
74
+ }
75
+ });
76
+
77
+ // Mock methods from mixins
78
+ jest.spyOn((wrapper.vm as any), 'fetchChart').mockImplementation().mockResolvedValue(undefined);
79
+ jest.spyOn((wrapper.vm as any), 'fetchAutoInstallInfo').mockImplementation().mockResolvedValue(undefined);
80
+ jest.spyOn((wrapper.vm as any), 'getClusterRegistry').mockImplementation().mockResolvedValue(undefined);
81
+ jest.spyOn((wrapper.vm as any), 'getGlobalRegistry').mockImplementation().mockResolvedValue(undefined);
82
+ jest.spyOn((wrapper.vm as any), 'loadValuesComponent').mockImplementation().mockResolvedValue(undefined);
83
+ jest.spyOn((wrapper.vm as any), 'updateStepOneReady').mockImplementation();
84
+
85
+ // Trigger fetch
86
+ await Install.fetch.call(wrapper.vm);
87
+
88
+ expect(wrapper.vm.forceNamespace).toBe('custom-ns');
89
+ expect(wrapper.vm.value.metadata.name).toBe('custom-name');
90
+ });
91
+ });
@@ -166,10 +166,10 @@ export default {
166
166
  } else if (this.$route.query[FROM_CLUSTER] === _FLAGGED) {
167
167
  /* For Fleet, use the fleet-default namespace. */
168
168
  this.forceNamespace = DEFAULT_WORKSPACE;
169
- } else if ( this.chart?.targetNamespace ) {
169
+ } else if ( this.version?.annotations?.[CATALOG_ANNOTATIONS.NAMESPACE] ) {
170
170
  /* If a target namespace is defined in the chart,
171
171
  set the target namespace as default. */
172
- this.forceNamespace = this.chart.targetNamespace;
172
+ this.forceNamespace = this.version.annotations[CATALOG_ANNOTATIONS.NAMESPACE];
173
173
  } else if ( this.query.appNamespace ) {
174
174
  /* If a namespace is defined in the URL query,
175
175
  use that namespace as default. */
@@ -219,13 +219,13 @@ export default {
219
219
  The target name indicates the name of the cluster
220
220
  group that the chart is meant to be installed in.
221
221
  */
222
- if ( this.chart?.targetName ) {
222
+ if ( this.version?.annotations?.[CATALOG_ANNOTATIONS.RELEASE_NAME] ) {
223
223
  /*
224
224
  Set the name of the chartInstallAction
225
225
  to the name of the cluster group
226
226
  where the chart should be installed.
227
227
  */
228
- this.value.metadata.name = this.chart.targetName;
228
+ this.value.metadata.name = this.version.annotations[CATALOG_ANNOTATIONS.RELEASE_NAME];
229
229
  this.nameDisabled = true;
230
230
  } else if ( this.query.appName ) {
231
231
  this.value.metadata.name = this.query.appName;
@@ -170,8 +170,8 @@ export default {
170
170
  <rc-dropdown-trigger
171
171
  data-testid="events-list-row-count-menu-toggle"
172
172
  :aria-label="t('glance.changeEventsListRowCount')"
173
- ghost
174
- small
173
+ variant="ghost"
174
+ size="small"
175
175
  >
176
176
  <i class="icon icon-gear" />
177
177
  </rc-dropdown-trigger>
@@ -1,11 +1,11 @@
1
- <script>
1
+ <script lang="ts">
2
2
  import debounce from 'lodash/debounce';
3
3
  import { getVersionData } from '@shell/config/version';
4
4
  import { mapState, mapGetters } from 'vuex';
5
5
  import { isEmpty } from '@shell/utils/object';
6
6
  import { FLEET } from '@shell/config/types';
7
7
  import { WORKSPACE } from '@shell/store/prefs';
8
- import Loading from '@shell/components/Loading';
8
+ import Loading from '@shell/components/Loading.vue';
9
9
  import { checkPermissions, checkSchemasForFindAllHash } from '@shell/utils/auth';
10
10
  import { WORKSPACE_ANNOTATION } from '@shell/config/labels-annotations';
11
11
  import { filterBy } from '@shell/utils/array';
@@ -15,7 +15,7 @@ import ResourcePanel from '@shell/components/fleet/dashboard/ResourcePanel.vue';
15
15
  import ResourceCard from '@shell/components/fleet/dashboard/ResourceCard.vue';
16
16
  import ResourceDetails from '@shell/components/fleet/dashboard/ResourceDetails.vue';
17
17
  import EmptyDashboard from '@shell/components/fleet/dashboard/Empty.vue';
18
- import ButtonGroup from '@shell/components/ButtonGroup';
18
+ import ButtonGroup from '@shell/components/ButtonGroup.vue';
19
19
  import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
20
20
  import FleetApplications from '@shell/components/fleet/FleetApplications.vue';
21
21
  import FleetUtils from '@shell/utils/fleet';
@@ -322,13 +322,19 @@ export default {
322
322
  this.selectedCard = selected;
323
323
 
324
324
  this.$shell.slideIn.open(ResourceDetails, {
325
- componentProps: {
326
- showHeader: false,
327
- width: window.innerWidth / 3 > 530 ? `${ window.innerWidth / 3 }px` : '530px',
325
+ showHeader: false,
326
+ props: {
328
327
  value,
329
328
  statePanel,
330
329
  workspace
331
- }
330
+ },
331
+ width: '73%',
332
+ // We want this to be full viewport height top to bottom
333
+ height: '100vh',
334
+ top: '0',
335
+ 'z-index': 101, // We want this to be above the main side menu
336
+ closeOnRouteChange: ['name', 'params', 'query'], // We want to ignore hash changes, tables in extensions can trigger the drawer to close while opening
337
+ triggerFocusTrap: false,
332
338
  });
333
339
  },
334
340
 
@@ -493,8 +499,8 @@ export default {
493
499
  @update:value="viewMode = $event"
494
500
  />
495
501
  <RcButton
496
- small
497
- ghost
502
+ size="small"
503
+ variant="ghost"
498
504
  data-testid="fleet-dashboard-expand-all"
499
505
  class="collapse-all-btn"
500
506
  @click="toggleCardAll(allCardsExpanded ? 'collapse' : 'expand')"
@@ -579,8 +585,8 @@ export default {
579
585
  :data-testid="'expand-button'"
580
586
  >
581
587
  <RcButton
582
- small
583
- ghost
588
+ size="small"
589
+ variant="ghost"
584
590
  :aria-label="`workspace-expand-btn-${ workspace.id }`"
585
591
  :data-testid="`workspace-expand-btn-${ workspace.id }`"
586
592
  @click="toggleCard(workspace.id)"
@@ -646,7 +652,7 @@ export default {
646
652
  class="create-button"
647
653
  >
648
654
  <RcButton
649
- small
655
+ size="small"
650
656
  @click="createResource(workspace.id)"
651
657
  >
652
658
  {{ t('fleet.application.intro.add') }}
@@ -7,7 +7,6 @@ import Masthead from '@shell/components/ResourceList/Masthead';
7
7
  import Banner from '@components/Banner/Banner.vue';
8
8
  import RcStatusBadge from '@components/Pill/RcStatusBadge/RcStatusBadge.vue';
9
9
  import { exceptionToErrorsArray } from '@shell/utils/error';
10
- import { isRancherPrime } from '@shell/config/version';
11
10
  import { stateDisplay, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
12
11
  import { getHostedProviders } from '@shell/utils/provider';
13
12
 
@@ -23,7 +22,6 @@ export default {
23
22
  allProviders: null,
24
23
  resource: HOSTED_PROVIDER,
25
24
  schema: this.$store.getters['rancher/schemaFor'](HOSTED_PROVIDER),
26
- prime: isRancherPrime(),
27
25
  settingResource: null
28
26
  };
29
27
  },
@@ -107,14 +105,13 @@ export default {
107
105
  async generateRows() {
108
106
  this.rows = this.allProviders.map((p) => {
109
107
  const active = p.id in this.settings ? this.settings[p.id] : true;
110
- const canNotPrime = p.prime && !this.prime;
111
108
  const canNotChangeSettings = !this.settingResource?.canUpdate;
112
109
  const enableAction = {
113
110
  action: 'activate',
114
111
  label: this.t('action.activate'),
115
112
  icon: 'icon icon-play',
116
113
  bulkable: true,
117
- enabled: !active && !canNotPrime && !canNotChangeSettings,
114
+ enabled: !active && !canNotChangeSettings,
118
115
  invoke: async(opts, resources) => {
119
116
  await this.setSetting(resources, true);
120
117
  }
@@ -137,7 +134,6 @@ export default {
137
134
  name: p.label,
138
135
  nameDisplay: p.label,
139
136
  description: p.description || '',
140
- prime: p.prime,
141
137
  active,
142
138
  availableActions
143
139
  };
@@ -192,13 +188,6 @@ export default {
192
188
  <div class="col">
193
189
  <div class="row">
194
190
  <span class="mr-10">{{ row.name }}</span>
195
- <RcStatusBadge
196
- v-if="row.prime"
197
- class="prime-badge"
198
- status="success"
199
- >
200
- {{ t('providers.hosted.prime') }}
201
- </RcStatusBadge>
202
191
  </div>
203
192
  <div
204
193
  v-if="row.description"
@@ -211,10 +200,3 @@ export default {
211
200
  </ResourceTable>
212
201
  </div>
213
202
  </template>
214
-
215
- <style lang="scss" scoped>
216
- .prime-badge {
217
- font-size: 10px;
218
- line-height: 15px;
219
- }
220
- </style>
@@ -349,7 +349,7 @@ export default {
349
349
  v-for="btn in panelActions"
350
350
  :key="btn.action"
351
351
  class="mmr-3 mmb-3"
352
- :[btn.role]="true"
352
+ :variant="btn.role"
353
353
  @click="onButtonClick(btn)"
354
354
  >
355
355
  <i :class="['icon', btn.icon, 'mmr-2']" />{{ btn.label }}
@@ -917,7 +917,7 @@ export default {
917
917
  <div v-if="hasFeatureFlag && hasMenuActions">
918
918
  <ActionMenu
919
919
  data-testid="extensions-page-menu"
920
- button-role="tertiary"
920
+ button-variant="tertiary"
921
921
  :button-aria-label="t('plugins.labels.menu')"
922
922
  :custom-actions="menuActions"
923
923
  @devLoad="showDeveloperLoadDialog"
@@ -1,6 +1,7 @@
1
1
  import jsyaml from 'js-yaml';
2
2
  import Resource from '@shell/plugins/dashboard-store/resource-class.js';
3
3
  import { resourceClassJunkObject } from '@shell/plugins/dashboard-store/__tests__/utils/store-mocks';
4
+ import { EVENT } from '@shell/config/types';
4
5
 
5
6
  describe('class: Resource', () => {
6
7
  describe('given custom resource keys', () => {
@@ -237,4 +238,237 @@ describe('class: Resource', () => {
237
238
  expect(mockStore.dispatch).not.toHaveBeenCalledWith('load', expect.any(Object));
238
239
  });
239
240
  });
241
+
242
+ describe('getter: resourceConditions', () => {
243
+ it('should return empty array when status.conditions is undefined', () => {
244
+ const resource = new Resource({ type: 'test' }, {
245
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
246
+ dispatch: jest.fn(),
247
+ rootGetters: { 'i18n/t': jest.fn() },
248
+ });
249
+
250
+ expect(resource.resourceConditions).toStrictEqual([]);
251
+ });
252
+
253
+ it('should return empty array when status is undefined', () => {
254
+ const resource = new Resource({ type: 'test', status: {} }, {
255
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
256
+ dispatch: jest.fn(),
257
+ rootGetters: { 'i18n/t': jest.fn() },
258
+ });
259
+
260
+ expect(resource.resourceConditions).toStrictEqual([]);
261
+ });
262
+
263
+ it('should map conditions correctly', () => {
264
+ const resource = new Resource({
265
+ type: 'test',
266
+ status: {
267
+ conditions: [
268
+ {
269
+ type: 'Ready', status: 'True', message: 'Resource is ready', lastTransitionTime: '2024-01-01T00:00:00Z'
270
+ },
271
+ ]
272
+ }
273
+ }, {
274
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
275
+ dispatch: jest.fn(),
276
+ rootGetters: { 'i18n/t': jest.fn() },
277
+ });
278
+
279
+ const conditions = resource.resourceConditions;
280
+
281
+ expect(conditions).toHaveLength(1);
282
+ expect(conditions[0].condition).toBe('Ready');
283
+ expect(conditions[0].status).toBe('True');
284
+ expect(conditions[0].message).toBe('Resource is ready');
285
+ expect(conditions[0].stateSimpleColor).toBe('disabled');
286
+ expect(conditions[0].time).toBe('2024-01-01T00:00:00Z');
287
+ });
288
+
289
+ it('should set error color when condition has error', () => {
290
+ const resource = new Resource({
291
+ type: 'test',
292
+ status: {
293
+ conditions: [
294
+ {
295
+ type: 'Ready', status: 'False', error: true, message: 'Something failed'
296
+ },
297
+ ]
298
+ }
299
+ }, {
300
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
301
+ dispatch: jest.fn(),
302
+ rootGetters: { 'i18n/t': jest.fn() },
303
+ });
304
+
305
+ const conditions = resource.resourceConditions;
306
+
307
+ expect(conditions[0].stateSimpleColor).toBe('error');
308
+ expect(conditions[0].error).toBe(true);
309
+ });
310
+
311
+ it('should prepend reason to message when present', () => {
312
+ const resource = new Resource({
313
+ type: 'test',
314
+ status: {
315
+ conditions: [
316
+ {
317
+ type: 'Ready', status: 'False', reason: 'MinimumReplicasUnavailable', message: 'Deployment does not have minimum availability.'
318
+ },
319
+ ]
320
+ }
321
+ }, {
322
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
323
+ dispatch: jest.fn(),
324
+ rootGetters: { 'i18n/t': jest.fn() },
325
+ });
326
+
327
+ const conditions = resource.resourceConditions;
328
+
329
+ expect(conditions[0].message).toBe('[MinimumReplicasUnavailable] Deployment does not have minimum availability.');
330
+ });
331
+
332
+ it('should use Unknown for missing type and status', () => {
333
+ const resource = new Resource({
334
+ type: 'test',
335
+ status: {
336
+ conditions: [
337
+ { message: 'Some message' },
338
+ ]
339
+ }
340
+ }, {
341
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
342
+ dispatch: jest.fn(),
343
+ rootGetters: { 'i18n/t': jest.fn() },
344
+ });
345
+
346
+ const conditions = resource.resourceConditions;
347
+
348
+ expect(conditions[0].condition).toBe('Unknown');
349
+ expect(conditions[0].status).toBe('Unknown');
350
+ });
351
+
352
+ it('should prioritize time fields correctly', () => {
353
+ const resource = new Resource({
354
+ type: 'test',
355
+ status: {
356
+ conditions: [
357
+ {
358
+ type: 'Test', lastProbeTime: 'probe-time', lastUpdateTime: 'update-time', lastTransitionTime: 'transition-time'
359
+ },
360
+ ]
361
+ }
362
+ }, {
363
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
364
+ dispatch: jest.fn(),
365
+ rootGetters: { 'i18n/t': jest.fn() },
366
+ });
367
+
368
+ const conditions = resource.resourceConditions;
369
+
370
+ expect(conditions[0].time).toBe('probe-time');
371
+ });
372
+ });
373
+
374
+ describe('getter: resourceEvents', () => {
375
+ it('should return events from the store', () => {
376
+ const mockEvents = [{ type: 'Normal', reason: 'Test' }];
377
+ const resource = new Resource({ type: 'test' }, {
378
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
379
+ dispatch: jest.fn(),
380
+ rootGetters: { 'i18n/t': jest.fn(), 'cluster/all': (type: string) => (type === EVENT ? mockEvents : []) },
381
+ });
382
+
383
+ expect(resource.resourceEvents).toStrictEqual(mockEvents);
384
+ });
385
+ });
386
+
387
+ describe('getter: cards', () => {
388
+ it('should return an array containing the insight card', () => {
389
+ const resource = new Resource({ type: 'test' }, {
390
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
391
+ dispatch: jest.fn(),
392
+ rootGetters: {
393
+ 'i18n/t': (key: string) => key,
394
+ 'cluster/all': () => []
395
+ },
396
+ });
397
+
398
+ const cards = resource.cards;
399
+
400
+ expect(cards).toHaveLength(1);
401
+ expect(cards[0]).toHaveProperty('component');
402
+ expect(cards[0]).toHaveProperty('props');
403
+ });
404
+ });
405
+
406
+ describe('getter: insightCardProps', () => {
407
+ it('should return props with title and rows', () => {
408
+ const resource = new Resource({
409
+ type: 'test',
410
+ status: {
411
+ conditions: [
412
+ { type: 'Ready', status: 'True' }
413
+ ]
414
+ }
415
+ }, {
416
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
417
+ dispatch: jest.fn(),
418
+ rootGetters: {
419
+ 'i18n/t': (key: string) => key,
420
+ 'cluster/all': () => []
421
+ },
422
+ });
423
+
424
+ const props = resource.insightCardProps;
425
+
426
+ expect(props.title).toBe('component.resource.detail.card.insightsCard.title');
427
+ expect(props.rows).toHaveLength(2);
428
+ expect(props.rows[0].label).toBe('component.resource.detail.card.insightsCard.rows.conditions');
429
+ expect(props.rows[1].label).toBe('component.resource.detail.card.insightsCard.rows.events');
430
+ });
431
+ });
432
+
433
+ describe('getter: detailPageAdditionalActions', () => {
434
+ it('should return undefined by default', () => {
435
+ const resource = new Resource({ type: 'test-type' }, {
436
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
437
+ dispatch: jest.fn(),
438
+ rootGetters: { 'i18n/t': jest.fn() },
439
+ });
440
+
441
+ expect(resource.detailPageAdditionalActions).toBeUndefined();
442
+ });
443
+
444
+ it('should allow subclasses to override and return button props array', () => {
445
+ class CustomResource extends Resource {
446
+ get detailPageAdditionalActions() {
447
+ return [
448
+ {
449
+ label: 'Action 1', variant: 'secondary', onClick: () => {}
450
+ },
451
+ {
452
+ label: 'Action 2', variant: 'primary', onClick: () => {}
453
+ }
454
+ ];
455
+ }
456
+ }
457
+
458
+ const resource = new CustomResource({ type: 'test-type' }, {
459
+ getters: { schemaFor: () => ({ linkFor: jest.fn() }) },
460
+ dispatch: jest.fn(),
461
+ rootGetters: { 'i18n/t': jest.fn() },
462
+ });
463
+
464
+ const actions = resource.detailPageAdditionalActions;
465
+
466
+ expect(Array.isArray(actions)).toBe(true);
467
+ expect(actions).toHaveLength(2);
468
+ expect(actions[0].label).toBe('Action 1');
469
+ expect(actions[0].variant).toBe('secondary');
470
+ expect(actions[1].label).toBe('Action 2');
471
+ expect(actions[1].variant).toBe('primary');
472
+ });
473
+ });
240
474
  });
@@ -471,7 +471,7 @@ export default {
471
471
  return findAllGetter(getters, type, opt);
472
472
  }
473
473
 
474
- console.log(`Find Page: [${ ctx.state.config.namespace }] ${ type }. Page: ${ opt.pagination.page }. Size: ${ opt.pagination.pageSize }. Sort: ${ opt.pagination.sort.map((s) => s.field).join(', ') }`); // eslint-disable-line no-console
474
+ console.log(`Find Page: [${ ctx.state.config.namespace }] ${ type }. Page: ${ opt.pagination.page }. Revision: ${ opt.revision || 'none' }. Size: ${ opt.pagination.pageSize }. Sort: ${ opt.pagination.sort.map((s) => s.field).join(', ') }`); // eslint-disable-line no-console
475
475
  opt = opt || {};
476
476
  opt.url = getters.urlFor(type, null, opt);
477
477
 
@@ -491,7 +491,7 @@ export default {
491
491
  return Promise.reject(e);
492
492
  }
493
493
 
494
- // Of type @StorePagination
494
+ // Of type @StorePaginationResult
495
495
  const pagination = opt.pagination ? {
496
496
  request: {
497
497
  namespace: opt.namespaced,
@@ -500,7 +500,8 @@ export default {
500
500
  result: {
501
501
  count: out.count,
502
502
  pages: out.pages || Math.ceil(out.count / (opt.pagination.pageSize || Number.MAX_SAFE_INTEGER)),
503
- timestamp: new Date().getTime()
503
+ timestamp: new Date().getTime(),
504
+ revision: out.revision
504
505
  }
505
506
  } : undefined;
506
507
 
@@ -698,19 +699,19 @@ export default {
698
699
 
699
700
  const res = await dispatch('request', { opt, type });
700
701
 
701
- await dispatch('load', { data: res, invalidatePageCache: opt.invalidatePageCache });
702
+ if (!opt.transient) {
703
+ await dispatch('load', { data: res, invalidatePageCache: opt.invalidatePageCache });
704
+ }
702
705
 
703
- if ( opt.watch !== false ) {
706
+ if (!opt.transient && opt.watch !== false ) {
704
707
  dispatch('watch', createFindWatchArg({
705
708
  type, id, opt, res
706
709
  }));
707
710
  }
708
711
 
709
- out = getters.byId(type, id);
710
-
711
712
  garbageCollect.gcUpdateLastAccessed(ctx, type);
712
713
 
713
- return out;
714
+ return opt.transient ? await dispatch('create', res) : getters.byId(type, id);
714
715
  },
715
716
 
716
717
  /**