@rancher/shell 3.0.5-rc.3 → 3.0.5-rc.5

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 (200) hide show
  1. package/assets/images/icons/document.svg +3 -0
  2. package/assets/images/vendor/cognito.svg +1 -0
  3. package/assets/styles/app.scss +1 -0
  4. package/assets/styles/base/_basic.scss +10 -0
  5. package/assets/styles/base/_spacing.scss +29 -0
  6. package/assets/styles/global/_layout.scss +1 -1
  7. package/assets/styles/themes/_dark.scss +25 -0
  8. package/assets/styles/themes/_light.scss +65 -0
  9. package/assets/translations/en-us.yaml +322 -24
  10. package/assets/translations/zh-hans.yaml +8 -5
  11. package/components/Certificates.vue +5 -0
  12. package/components/FilterPanel.vue +156 -0
  13. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  14. package/components/IconOrSvg.vue +14 -35
  15. package/components/PromptRemove.vue +5 -1
  16. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  17. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  18. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  19. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  20. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  21. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  22. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  23. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  24. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  25. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  26. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  27. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  28. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  29. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  30. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  31. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  32. package/components/Resource/Detail/Card/index.vue +56 -0
  33. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  34. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  35. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  36. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  37. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  39. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  40. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  41. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  42. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  43. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  44. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  45. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  46. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  47. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  48. package/components/Resource/Detail/Metadata/index.vue +66 -0
  49. package/components/Resource/Detail/Page.vue +22 -0
  50. package/components/Resource/Detail/PercentageBar.vue +40 -0
  51. package/components/Resource/Detail/ResourceRow.vue +119 -0
  52. package/components/Resource/Detail/SpacedRow.vue +14 -0
  53. package/components/Resource/Detail/StatusBar.vue +59 -0
  54. package/components/Resource/Detail/StatusRow.vue +61 -0
  55. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  56. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  57. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  58. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  59. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  60. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  61. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  62. package/components/Resource/Detail/Top/index.vue +34 -0
  63. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  64. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  65. package/components/ResourceDetail/index.vue +64 -562
  66. package/components/ResourceDetail/legacy.vue +545 -0
  67. package/components/ResourceTable.vue +41 -7
  68. package/components/SlideInPanelManager.vue +76 -8
  69. package/components/SortableTable/index.vue +13 -2
  70. package/components/SortableTable/selection.js +21 -8
  71. package/components/StatusBadge.vue +6 -4
  72. package/components/SubtleLink.vue +25 -0
  73. package/components/Wizard.vue +12 -1
  74. package/components/YamlEditor.vue +1 -1
  75. package/components/__tests__/FilterPanel.test.ts +81 -0
  76. package/components/auth/AuthBanner.vue +2 -3
  77. package/components/auth/RoleDetailEdit.vue +45 -3
  78. package/components/auth/login/oidc.vue +6 -1
  79. package/components/fleet/FleetApplications.vue +181 -0
  80. package/components/fleet/FleetHelmOps.vue +115 -0
  81. package/components/fleet/FleetIntro.vue +58 -28
  82. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  83. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  84. package/components/fleet/FleetRepos.vue +38 -76
  85. package/components/fleet/FleetResources.vue +50 -22
  86. package/components/fleet/FleetSummary.vue +26 -51
  87. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  88. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  89. package/components/fleet/dashboard/Empty.vue +73 -0
  90. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  91. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  92. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  93. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  94. package/components/form/ArrayList.vue +6 -0
  95. package/components/form/SimpleSecretSelector.vue +8 -2
  96. package/components/form/ValueFromResource.vue +31 -19
  97. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  98. package/components/formatter/FleetApplicationSource.vue +71 -0
  99. package/components/formatter/FleetSummaryGraph.vue +7 -0
  100. package/components/nav/Header.vue +8 -7
  101. package/components/nav/TopLevelMenu.helper.ts +55 -34
  102. package/components/nav/TopLevelMenu.vue +11 -0
  103. package/components/nav/Type.vue +4 -1
  104. package/composables/useI18n.ts +12 -11
  105. package/config/labels-annotations.js +14 -11
  106. package/config/product/auth.js +1 -0
  107. package/config/product/fleet.js +70 -17
  108. package/config/query-params.js +3 -1
  109. package/config/roles.ts +1 -0
  110. package/config/router/routes.js +20 -2
  111. package/config/secret.ts +15 -0
  112. package/config/settings.ts +3 -2
  113. package/config/table-headers.js +52 -22
  114. package/config/types.js +2 -0
  115. package/core/plugin-helpers.ts +3 -2
  116. package/detail/fleet.cattle.io.cluster.vue +28 -15
  117. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  118. package/detail/fleet.cattle.io.helmop.vue +157 -0
  119. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  120. package/dialog/RedeployWorkloadDialog.vue +164 -0
  121. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  122. package/edit/auth/oidc.vue +159 -93
  123. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  124. package/edit/fleet.cattle.io.helmop.vue +997 -0
  125. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  126. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  127. package/list/fleet.cattle.io.helmop.vue +108 -0
  128. package/list/namespace.vue +5 -2
  129. package/mixins/auth-config.js +8 -1
  130. package/mixins/preset.js +100 -0
  131. package/mixins/resource-fetch-api-pagination.js +2 -0
  132. package/mixins/resource-fetch.js +1 -1
  133. package/mixins/resource-table-watch.js +45 -0
  134. package/models/__tests__/chart.test.ts +273 -0
  135. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  136. package/models/chart.js +144 -2
  137. package/models/fleet-application.js +385 -0
  138. package/models/fleet.cattle.io.bundle.js +9 -8
  139. package/models/fleet.cattle.io.gitrepo.js +41 -365
  140. package/models/fleet.cattle.io.helmop.js +228 -0
  141. package/models/management.cattle.io.authconfig.js +1 -0
  142. package/models/management.cattle.io.fleetworkspace.js +12 -0
  143. package/models/workload.js +14 -18
  144. package/package.json +2 -1
  145. package/pages/auth/verify.vue +13 -1
  146. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  147. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  148. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  149. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  150. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  151. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  152. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  153. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  154. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  155. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  156. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  157. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  158. package/pages/c/_cluster/fleet/index.vue +772 -330
  159. package/pages/explorer/resource/detail/configmap.vue +19 -0
  160. package/plugins/dashboard-store/actions.js +31 -9
  161. package/plugins/dashboard-store/getters.js +34 -21
  162. package/plugins/dashboard-store/mutations.js +51 -7
  163. package/plugins/dashboard-store/resource-class.js +14 -2
  164. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  165. package/plugins/steve/actions.js +3 -0
  166. package/plugins/steve/steve-pagination-utils.ts +14 -13
  167. package/plugins/steve/subscribe.js +229 -42
  168. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  169. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  170. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  171. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  172. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  173. package/rancher-components/RcItemCard/index.ts +2 -0
  174. package/store/auth.js +1 -0
  175. package/store/catalog.js +62 -24
  176. package/store/index.js +33 -14
  177. package/store/slideInPanel.ts +6 -0
  178. package/store/type-map.js +1 -0
  179. package/types/fleet.d.ts +35 -0
  180. package/types/resources/settings.d.ts +19 -1
  181. package/types/shell/index.d.ts +339 -272
  182. package/types/store/dashboard-store.types.ts +17 -3
  183. package/types/store/pagination.types.ts +6 -1
  184. package/types/store/subscribe.types.ts +50 -0
  185. package/utils/auth.js +32 -3
  186. package/utils/fleet-types.ts +0 -0
  187. package/utils/fleet.ts +200 -1
  188. package/utils/pagination-utils.ts +26 -1
  189. package/utils/pagination-wrapper.ts +132 -50
  190. package/utils/settings.ts +4 -1
  191. package/utils/style.ts +39 -0
  192. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  193. package/utils/validators/formRules/index.ts +10 -3
  194. package/utils/window.js +11 -7
  195. package/components/__tests__/ApplicationCard.test.ts +0 -27
  196. package/components/cards/ApplicationCard.vue +0 -145
  197. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  198. package/config/secret.js +0 -14
  199. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  200. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -1,10 +1,12 @@
1
1
  <script>
2
+ import { _CREATE } from '@shell/config/query-params';
2
3
  import CreateEditView from '@shell/mixins/create-edit-view';
3
4
  import CruResource from '@shell/components/CruResource';
4
5
  import Labels from '@shell/components/form/Labels';
5
6
  import Loading from '@shell/components/Loading';
6
7
  import NameNsDescription from '@shell/components/form/NameNsDescription';
7
8
  import { FLEET, MANAGEMENT, SCHEMA } from '@shell/config/types';
9
+ import { FLEET as FLEET_ANNOTATIONS } from '@shell/config/labels-annotations';
8
10
  // import RoleBindings from '@shell/components/RoleBindings';
9
11
  import Tabbed from '@shell/components/Tabbed';
10
12
  import Tab from '@shell/components/Tabbed/Tab';
@@ -16,6 +18,7 @@ import { LAST_NAMESPACE, WORKSPACE } from '@shell/store/prefs';
16
18
  import { exceptionToErrorsArray } from '@shell/utils/error';
17
19
  import Banner from '@components/Banner/Banner.vue';
18
20
  import ArrayList from '@shell/components/form/ArrayList.vue';
21
+ import FleetOCIStorageSecret from '@shell/components/fleet/FleetOCIStorageSecret.vue';
19
22
 
20
23
  export default {
21
24
  name: 'FleetCruWorkspace',
@@ -25,6 +28,7 @@ export default {
25
28
  inheritAttrs: false,
26
29
  components: {
27
30
  CruResource,
31
+ FleetOCIStorageSecret,
28
32
  Labels,
29
33
  Loading,
30
34
  NameNsDescription,
@@ -126,6 +130,10 @@ export default {
126
130
  this.errors = exceptionToErrorsArray(err);
127
131
  }
128
132
  },
133
+
134
+ updateDefaultOCIStorageSecret(secretName) {
135
+ this.value.metadata.annotations[FLEET_ANNOTATIONS.OCI_STORAGE_SECRET_DEFAULT] = secretName;
136
+ }
129
137
  },
130
138
 
131
139
  computed: {
@@ -145,6 +153,14 @@ export default {
145
153
  }
146
154
  },
147
155
 
156
+ defaultOCIStorageSecret() {
157
+ return this.value.metadata?.annotations?.[FLEET_ANNOTATIONS.OCI_STORAGE_SECRET_DEFAULT];
158
+ },
159
+
160
+ isCreate() {
161
+ return this.mode === _CREATE;
162
+ },
163
+
148
164
  SCOPE_NAMESPACE() {
149
165
  return SCOPE_NAMESPACE;
150
166
  },
@@ -198,19 +214,10 @@ export default {
198
214
  />
199
215
  </Tab> -->
200
216
 
201
- <Tab
202
- name="labels"
203
- label-key="generic.labelsAndAnnotations"
204
- >
205
- <Labels
206
- :value="value"
207
- :mode="mode"
208
- @update:value="$emit('input', $event)"
209
- />
210
- </Tab>
211
217
  <Tab
212
218
  name="allowedtargetnamespaces"
213
219
  label-key="fleet.workspaces.tabs.restrictions"
220
+ :weight="3"
214
221
  >
215
222
  <Banner
216
223
  color="info"
@@ -240,6 +247,32 @@ export default {
240
247
  :value-can-be-empty="true"
241
248
  />
242
249
  </Tab>
250
+ <Tab
251
+ v-if="!isCreate"
252
+ name="ociRegistries"
253
+ label-key="fleet.workspaces.tabs.ociRegistry"
254
+ :weight="2"
255
+ >
256
+ <FleetOCIStorageSecret
257
+ data-testid="default-oci-storage-secret"
258
+ :secret="defaultOCIStorageSecret"
259
+ :workspace="value.metadata.name"
260
+ :mode="mode"
261
+ :allow-default="false"
262
+ @update:value="updateDefaultOCIStorageSecret"
263
+ />
264
+ </Tab>
265
+ <Tab
266
+ name="labels"
267
+ label-key="generic.labelsAndAnnotations"
268
+ :weight="1"
269
+ >
270
+ <Labels
271
+ :value="value"
272
+ :mode="mode"
273
+ @update:value="$emit('input', $event)"
274
+ />
275
+ </Tab>
243
276
  </Tabbed>
244
277
  </CruResource>
245
278
  </template>
@@ -65,7 +65,7 @@ export default {
65
65
 
66
66
  }, this.$store);
67
67
 
68
- this.hasWorkspaces = !!hash.workspaces;
68
+ this.hasWorkspaces = (hash.workspaces || []).length > 0;
69
69
  } catch (e) {
70
70
  }
71
71
 
@@ -0,0 +1,108 @@
1
+ <script>
2
+ import FleetHelmOps from '@shell/components/fleet/FleetHelmOps.vue';
3
+ import Masthead from '@shell/components/ResourceList/Masthead';
4
+ import FleetNoWorkspaces from '@shell/components/fleet/FleetNoWorkspaces.vue';
5
+ import { FLEET } from '@shell/config/types';
6
+ import ResourceFetch from '@shell/mixins/resource-fetch';
7
+ import { checkPermissions, checkSchemasForFindAllHash } from '@shell/utils/auth';
8
+
9
+ export default {
10
+ name: 'ListHelmOps',
11
+ components: {
12
+ FleetHelmOps,
13
+ Masthead,
14
+ FleetNoWorkspaces,
15
+ },
16
+ mixins: [ResourceFetch],
17
+ props: {
18
+ schema: {
19
+ type: Object,
20
+ required: true,
21
+ },
22
+
23
+ resource: {
24
+ type: String,
25
+ required: true,
26
+ },
27
+
28
+ loadIndeterminate: {
29
+ type: Boolean,
30
+ default: false
31
+ },
32
+
33
+ incrementalLoadingIndicator: {
34
+ type: Boolean,
35
+ default: false
36
+ },
37
+
38
+ useQueryParamsForSimpleFiltering: {
39
+ type: Boolean,
40
+ default: false
41
+ }
42
+ },
43
+
44
+ async fetch() {
45
+ try {
46
+ const hash = await checkSchemasForFindAllHash({
47
+ cluster: {
48
+ inStoreType: 'management',
49
+ type: FLEET.CLUSTER
50
+ },
51
+ clusterGroups: {
52
+ inStoreType: 'management',
53
+ type: FLEET.CLUSTER_GROUP
54
+ },
55
+
56
+ helmOps: {
57
+ inStoreType: 'management',
58
+ type: FLEET.HELM_OP
59
+ },
60
+
61
+ workspaces: {
62
+ inStoreType: 'management',
63
+ type: FLEET.WORKSPACE
64
+ },
65
+
66
+ }, this.$store);
67
+
68
+ this.hasWorkspaces = (hash.workspaces || []).length > 0;
69
+ } catch (e) {
70
+ }
71
+
72
+ try {
73
+ const permissions = await checkPermissions({ workspaces: { type: FLEET.WORKSPACE }, helmOps: { type: FLEET.HELM_OP } }, this.$store.getters);
74
+
75
+ this.permissions = permissions;
76
+ } catch (e) {
77
+ }
78
+ await this.$fetchType(this.resource);
79
+ },
80
+
81
+ data() {
82
+ return { hasWorkspaces: false, permissions: {} };
83
+ },
84
+ };
85
+ </script>
86
+
87
+ <template>
88
+ <div v-if="hasWorkspaces">
89
+ <Masthead
90
+ :schema="schema"
91
+ :resource="resource"
92
+ :show-incremental-loading-indicator="incrementalLoadingIndicator"
93
+ :load-resources="loadResources"
94
+ :load-indeterminate="loadIndeterminate"
95
+ :create-button-label="t('fleet.helmOp.actions.add')"
96
+ />
97
+ <FleetHelmOps
98
+ :rows="rows"
99
+ :schema="schema"
100
+ :loading="loading"
101
+ :use-query-params-for-simple-filtering="useQueryParamsForSimpleFiltering"
102
+ :force-update-live-and-delayed="forceUpdateLiveAndDelayed"
103
+ />
104
+ </div>
105
+ <div v-else>
106
+ <FleetNoWorkspaces :can-view="permissions.workspaces" />
107
+ </div>
108
+ </template>
@@ -42,8 +42,11 @@ export default {
42
42
  headers() {
43
43
  const headersFromSchema = this.$store.getters['type-map/headersFor'](this.schema);
44
44
 
45
- if (this.hasHarvesterResourceQuotaSchema) {
46
- headersFromSchema.splice(2, 0, NS_SNAPSHOT_QUOTA);
45
+ // harvester is reusing this namespace.js to render ns page, we need to make sure harvester backend support quota schema to show this column.
46
+ if (this.hasHarvesterResourceQuotaSchema && Array.isArray(headersFromSchema) && headersFromSchema.length > 1) {
47
+ const columnIdx = headersFromSchema.length - 1;
48
+
49
+ headersFromSchema.splice(columnIdx, 0, NS_SNAPSHOT_QUOTA);
47
50
  }
48
51
 
49
52
  return headersFromSchema;
@@ -307,7 +307,14 @@ export default {
307
307
 
308
308
  // KeyCloakOIDCConfig --> OIDCConfig
309
309
  this.model.rancherUrl = `${ serverUrl }/verify-auth`;
310
- this.model.scope = this.model.id === 'keycloakoidc' ? BASE_SCOPES.keycloakoidc[0] : BASE_SCOPES.genericoidc[0];
310
+
311
+ // If there are base scopes defined for this provider, use those
312
+ if (Array.isArray(BASE_SCOPES[this.model.id])) {
313
+ this.model.scope = BASE_SCOPES[this.model.id][0];
314
+ } else {
315
+ // Default if base scopes not defined for this auth provider
316
+ this.model.scope = BASE_SCOPES.genericoidc[0];
317
+ }
311
318
  break;
312
319
  }
313
320
 
@@ -0,0 +1,100 @@
1
+ import { watch } from 'vue';
2
+ import { dasherize } from '@shell/utils/string';
3
+ import { NORMAN } from '@shell/config/types';
4
+
5
+ export default {
6
+ data() {
7
+ return {
8
+ _init: [],
9
+ presets: {},
10
+ };
11
+ },
12
+
13
+ mounted() {
14
+ this.presetKey = dasherize(this.$options.name || '');
15
+
16
+ const version = this.presetVersion;
17
+
18
+ if (!version) {
19
+ console.warn('Preset: version not found, skip.'); // eslint-disable-line no-console
20
+
21
+ return;
22
+ }
23
+
24
+ let presets = null;
25
+
26
+ try {
27
+ presets = JSON.parse(window.localStorage.getItem(this.presetKey));
28
+ } catch (error) {
29
+ console.warn(`Preset: load presets failed, invalid presets [${ this.presetKey }]`); // eslint-disable-line no-console
30
+ }
31
+
32
+ if (presets?.data && presets?.user === this.user && presets?.version === version) {
33
+ this.presets = presets;
34
+
35
+ return;
36
+ }
37
+
38
+ this.presets = {
39
+ data: {},
40
+ user: this.user,
41
+ version
42
+ };
43
+ },
44
+
45
+ computed: {
46
+ user() {
47
+ const principal = this.$store.getters['rancher/byId'](NORMAN.PRINCIPAL, this.$store.getters['auth/principalId']) || {};
48
+
49
+ return principal.loginName;
50
+ }
51
+ },
52
+
53
+ methods: {
54
+ preset(key, type) {
55
+ if (this._init.includes(key)) {
56
+ return;
57
+ }
58
+
59
+ this._init.push(key);
60
+
61
+ if (!this.presetVersion) {
62
+ return;
63
+ }
64
+
65
+ if (!this.presetKey) {
66
+ return;
67
+ }
68
+
69
+ if (!this.presets?.data) {
70
+ return;
71
+ }
72
+
73
+ if (this[key] === undefined) {
74
+ return;
75
+ }
76
+
77
+ const preset = this.presets.data[key];
78
+
79
+ if (preset !== undefined && typeof preset === type) { // eslint-disable-line valid-typeof
80
+ this[key] = preset;
81
+ }
82
+
83
+ watch(
84
+ () => this[key],
85
+ (neu) => {
86
+ this.presets.data[key] = neu;
87
+
88
+ try {
89
+ const presets = JSON.stringify(this.presets);
90
+
91
+ window.localStorage.setItem(this.presetKey, presets);
92
+ } catch (error) {
93
+ console.warn(`Preset: save presets failed, invalid presets [${ this.presetKey }]`); // eslint-disable-line no-console
94
+ }
95
+ },
96
+ { deep: true }
97
+ );
98
+ },
99
+ },
100
+ };
@@ -47,6 +47,8 @@ export default {
47
47
  },
48
48
 
49
49
  paginationFromList: null,
50
+
51
+ isPaginationManualRefreshEnabled: paginationUtils.isListManualRefreshEnabled({ rootGetters: this.$store.getters }),
50
52
  };
51
53
  },
52
54
 
@@ -280,7 +280,7 @@ export default {
280
280
  if (manualDataRefreshEnabled && resourceCount >= manualDataRefreshThreshold) {
281
281
  watch = false;
282
282
  isTooManyItemsToAutoUpdate = true;
283
- } else if (this.canPaginate) {
283
+ } else if (this.canPaginate && this.isPaginationManualRefreshEnabled) {
284
284
  isTooManyItemsToAutoUpdate = true;
285
285
  }
286
286
 
@@ -0,0 +1,45 @@
1
+ import { STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
2
+
3
+ // This is functionality used to stop or start sockets used to watch resources in the vai world.
4
+ // Currently disabled via (non-public) perf setting
5
+ // See https://github.com/rancher/dashboard/issues/14359 for long term plan
6
+
7
+ export default {
8
+ props: {
9
+ schema: {
10
+ type: Object,
11
+ default: null,
12
+ },
13
+ },
14
+
15
+ data() {
16
+ // Note - does not cover anything fetched by secondary / page functions
17
+ const watchOpts = this.schema?.id ? {
18
+ type: this.schema.id,
19
+ mode: STEVE_WATCH_MODE.RESOURCE_CHANGES
20
+ // Note - we don't restrict watch by namespace (would involve unwatch, request, watch with new revision on every change to ns filter)
21
+ } : undefined;
22
+
23
+ return { watchOpts };
24
+ },
25
+
26
+ computed: {
27
+ watching() {
28
+ return this.$store.getters[`${ this.inStore }/watchStarted`](this.watchOpts);
29
+ },
30
+ },
31
+
32
+ methods: {
33
+ toggleWatch(toggle) {
34
+ if (toggle) {
35
+ // Assume there's a gap between cache and reality, to restart watch with something that will make a new http request to refresh it
36
+ this.$store.dispatch(`${ this.inStore }/resyncWatch`, {
37
+ ...this.watchOpts,
38
+ resourceType: this.watchOpts?.type
39
+ });
40
+ } else {
41
+ this.$store.dispatch(`${ this.inStore }/unwatch`, this.watchOpts);
42
+ }
43
+ }
44
+ }
45
+ };
@@ -0,0 +1,273 @@
1
+ import Chart from '@shell/models/chart';
2
+ import { APP_UPGRADE_STATUS } from '@shell/store/catalog';
3
+ import { CATALOG as CATALOG_ANNOTATIONS } from '@shell/config/labels-annotations';
4
+
5
+ const base = {
6
+ chartName: 'my-app',
7
+ repoName: 'my-repo',
8
+ repoNameDisplay: 'My Repo',
9
+ versions: [
10
+ {
11
+ version: '1.3.0', home: 'https://example.com', created: '2024-03-10T12:00:00Z'
12
+ },
13
+ { version: '1.2.3', home: 'https://example.com' }
14
+ ],
15
+ categories: [],
16
+ tags: [],
17
+ deprecated: false
18
+ };
19
+
20
+ function makeInstalledApp(upgradeAvailable = APP_UPGRADE_STATUS.NO_UPGRADE) {
21
+ return {
22
+ spec: {
23
+ chart: {
24
+ metadata: {
25
+ name: 'my-app',
26
+ version: '1.3.0',
27
+ home: 'https://example.com',
28
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: 'my-repo' }
29
+ }
30
+ }
31
+ },
32
+ upgradeAvailable
33
+ };
34
+ }
35
+
36
+ describe('class Chart', () => {
37
+ describe('matchingInstalledApps', () => {
38
+ it('matches by name, repo, and home in latest version', () => {
39
+ const installedApp = makeInstalledApp();
40
+
41
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
42
+
43
+ expect(chart.matchingInstalledApps).toHaveLength(1);
44
+ });
45
+
46
+ it('does not match if name is different', () => {
47
+ const installedApp = makeInstalledApp();
48
+
49
+ installedApp.spec.chart.metadata.name = 'different-app';
50
+
51
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
52
+
53
+ expect(chart.matchingInstalledApps).toHaveLength(0);
54
+ });
55
+
56
+ it('does not match if repo is different', () => {
57
+ const installedApp = makeInstalledApp();
58
+
59
+ installedApp.spec.chart.metadata.annotations[CATALOG_ANNOTATIONS.SOURCE_REPO_NAME] = 'different-repo';
60
+
61
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
62
+
63
+ expect(chart.matchingInstalledApps).toHaveLength(0);
64
+ });
65
+
66
+ it('matches by version+home when not latest', () => {
67
+ const installedApp = makeInstalledApp();
68
+
69
+ installedApp.spec.chart.metadata.version = '1.2.3'; // not the latest in base
70
+
71
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
72
+
73
+ expect(chart.matchingInstalledApps).toHaveLength(1);
74
+ });
75
+
76
+ it('can use fallback repo from metadata labels', () => {
77
+ const installedApp = makeInstalledApp();
78
+
79
+ installedApp.spec.chart.metadata.annotations = {}; // remove SOURCE_REPO_NAME
80
+ installedApp.metadata = { labels: { [CATALOG_ANNOTATIONS.CLUSTER_REPO_NAME]: 'my-repo' } };
81
+
82
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
83
+
84
+ expect(chart.matchingInstalledApps).toHaveLength(1);
85
+ });
86
+ });
87
+
88
+ describe('isInstalled', () => {
89
+ it('is true when one app matches', () => {
90
+ const installedApp = {
91
+ spec: {
92
+ chart: {
93
+ metadata: {
94
+ name: 'my-app',
95
+ version: '1.2.3',
96
+ home: 'https://example.com',
97
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: 'my-repo' }
98
+ }
99
+ }
100
+ }
101
+ };
102
+
103
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
104
+
105
+ expect(chart.isInstalled).toBe(true);
106
+ });
107
+
108
+ it('is false when no apps match', () => {
109
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [] } });
110
+
111
+ expect(chart.isInstalled).toBe(false);
112
+ });
113
+
114
+ it('is false when multiple apps match', () => {
115
+ const appTemplate = {
116
+ spec: {
117
+ chart: {
118
+ metadata: {
119
+ name: 'my-app',
120
+ version: '1.2.3',
121
+ home: 'https://example.com',
122
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: 'my-repo' }
123
+ }
124
+ }
125
+ }
126
+ };
127
+
128
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [appTemplate, appTemplate] } });
129
+
130
+ expect(chart.isInstalled).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('upgradeable', () => {
135
+ it('is true when installed and upgradeAvailable is SINGLE_UPGRADE', () => {
136
+ const installedApp = {
137
+ spec: {
138
+ chart: {
139
+ metadata: {
140
+ name: 'my-app',
141
+ version: '1.2.3',
142
+ home: 'https://example.com',
143
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: 'my-repo' }
144
+ }
145
+ }
146
+ },
147
+ upgradeAvailable: APP_UPGRADE_STATUS.SINGLE_UPGRADE
148
+ };
149
+
150
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
151
+
152
+ expect(chart.upgradeable).toBe(true);
153
+ });
154
+
155
+ it('is false if upgradeAvailable is different', () => {
156
+ const installedApp = {
157
+ spec: {
158
+ chart: {
159
+ metadata: {
160
+ name: 'my-app',
161
+ version: '1.2.3',
162
+ home: 'https://example.com',
163
+ annotations: { [CATALOG_ANNOTATIONS.SOURCE_REPO_NAME]: 'my-repo' }
164
+ }
165
+ }
166
+ },
167
+ upgradeAvailable: APP_UPGRADE_STATUS.NO_UPGRADE
168
+ };
169
+
170
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
171
+
172
+ expect(chart.upgradeable).toBe(false);
173
+ });
174
+
175
+ it('is false when not installed', () => {
176
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [] } });
177
+
178
+ expect(chart.upgradeable).toBe(false);
179
+ });
180
+ });
181
+
182
+ describe('cardContent', () => {
183
+ it('includes correct subHeader and footer info', () => {
184
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [] } });
185
+
186
+ const result = chart.cardContent;
187
+
188
+ expect(result.subHeaderItems).toHaveLength(2);
189
+ expect(result.subHeaderItems[0].label).toBe('1.3.0');
190
+ expect(result.subHeaderItems[1].label).toBe('Mar 10, 2024');
191
+
192
+ expect(result.footerItems).toHaveLength(1);
193
+ expect(result.footerItems[0].labels).toContain('My Repo');
194
+
195
+ expect(result.statuses).toHaveLength(0);
196
+ });
197
+
198
+ it('includes category and tag items when present', () => {
199
+ const chart = new Chart({
200
+ ...base,
201
+ categories: ['database'],
202
+ tags: ['linux', 'experimentl']
203
+ }, { rootGetters: { 'cluster/all': () => [] } });
204
+
205
+ const result = chart.cardContent;
206
+
207
+ expect(result.footerItems).toHaveLength(3);
208
+
209
+ const categoryItem = result.footerItems.find((i) => i.icon === 'icon-category-alt');
210
+
211
+ expect(categoryItem).toBeDefined();
212
+ expect(categoryItem.labels).toContain('database');
213
+
214
+ const tagItem = result.footerItems.find((i) => i.icon === 'icon-tag-alt');
215
+
216
+ expect(tagItem).toBeDefined();
217
+ expect(tagItem.labels).toStrictEqual(expect.arrayContaining(['linux', 'experimentl']));
218
+ });
219
+
220
+ it('includes deprecated status when deprecated is true', () => {
221
+ const chart = new Chart({ ...base, deprecated: true }, { rootGetters: { 'cluster/all': () => [] } });
222
+
223
+ const result = chart.cardContent;
224
+
225
+ const deprecatedStatus = result.statuses.find((s) => s.tooltip.key === 'generic.deprecated');
226
+
227
+ expect(deprecatedStatus).toBeDefined();
228
+ expect(deprecatedStatus.color).toBe('error');
229
+ });
230
+
231
+ it('includes installed status when app is installed', () => {
232
+ const installedApp = makeInstalledApp();
233
+
234
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
235
+
236
+ const result = chart.cardContent;
237
+
238
+ const installedStatus = result.statuses.find((s) => s.tooltip.key === 'generic.installed');
239
+
240
+ expect(installedStatus).toBeDefined();
241
+ expect(installedStatus.color).toBe('success');
242
+ });
243
+
244
+ it('includes upgradeable status when upgrade is available', () => {
245
+ const installedApp = makeInstalledApp(APP_UPGRADE_STATUS.SINGLE_UPGRADE);
246
+
247
+ const chart = new Chart(base, { rootGetters: { 'cluster/all': () => [installedApp] } });
248
+
249
+ const result = chart.cardContent;
250
+
251
+ const upgradeableStatus = result.statuses.find((s) => s.tooltip.key === 'generic.upgradeable');
252
+
253
+ expect(upgradeableStatus).toBeDefined();
254
+ expect(upgradeableStatus.color).toBe('info');
255
+ });
256
+
257
+ it('shows all statuses together when all conditions are met', () => {
258
+ const installedApp = makeInstalledApp(APP_UPGRADE_STATUS.SINGLE_UPGRADE);
259
+
260
+ const chart = new Chart({ ...base, deprecated: true }, { rootGetters: { 'cluster/all': () => [installedApp] } });
261
+
262
+ const result = chart.cardContent;
263
+
264
+ const keys = result.statuses.map((s) => s.tooltip.key);
265
+
266
+ expect(keys).toStrictEqual(expect.arrayContaining([
267
+ 'generic.deprecated',
268
+ 'generic.upgradeable',
269
+ 'generic.installed'
270
+ ]));
271
+ });
272
+ });
273
+ });