@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
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import DetailPage from '@shell/components/Resource/Detail/Page.vue';
3
+ import { CONFIG_MAP } from '@shell/config/types';
4
+ import { useStore } from 'vuex';
5
+
6
+ const store = useStore();
7
+ const configMaps = await store.dispatch('cluster/findAll', { type: CONFIG_MAP });
8
+
9
+ </script>
10
+ <template>
11
+ <DetailPage>
12
+ <template #top-area>
13
+ ConfigMap
14
+ </template>
15
+ <template #middle-area>
16
+ {{ configMaps }}
17
+ </template>
18
+ </DetailPage>
19
+ </template>
@@ -9,6 +9,7 @@ import garbageCollect from '@shell/utils/gc/gc';
9
9
  import { addSchemaIndexFields } from '@shell/plugins/steve/schema.utils';
10
10
  import { addParam, parse } from '@shell/utils/url';
11
11
  import { conditionalDepaginate } from '@shell/store/type-map.utils';
12
+ import { STEVE_WATCH_MODE } from '@shell/types/store/subscribe.types';
12
13
  import { FilterArgs } from '@shell/types/store/pagination.types';
13
14
  import { isLabelSelectorEmpty, labelSelectorToSelector } from '@shell/utils/selector-typed';
14
15
 
@@ -183,7 +184,6 @@ export default {
183
184
 
184
185
  opt = opt || {};
185
186
  type = getters.normalizeType(type);
186
-
187
187
  if ( !getters.typeRegistered(type) ) {
188
188
  commit('registerType', type);
189
189
  }
@@ -426,8 +426,20 @@ export default {
426
426
  commit('registerType', type);
427
427
  }
428
428
 
429
+ // of type @STEVE_WATCH_PARAMS
430
+ const watchArgs = {
431
+ type,
432
+ namespace: opt.watchNamespace || opt.namespaced, // it could be either apparently
433
+ force: opt.forceWatch === true,
434
+ mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
435
+ };
436
+
429
437
  // No need to request the resources if we have them already
430
438
  if (!opt.transient && !opt.force && getters['havePaginatedPage'](type, opt)) {
439
+ if (opt.watch !== false ) {
440
+ dispatch('watch', watchArgs);
441
+ }
442
+
431
443
  return findAllGetter(getters, type, opt);
432
444
  }
433
445
 
@@ -451,11 +463,7 @@ export default {
451
463
  return Promise.reject(e);
452
464
  }
453
465
 
454
- await dispatch('unwatch', {
455
- type,
456
- all: true,
457
- });
458
-
466
+ // Of type @StorePagination
459
467
  const pagination = opt.pagination ? {
460
468
  request: {
461
469
  namespace: opt.namespaced,
@@ -472,11 +480,16 @@ export default {
472
480
  commit('loadPage', {
473
481
  ctx,
474
482
  type,
475
- data: out.data,
483
+ data: out.data,
476
484
  pagination,
485
+ revision: out.revision,
477
486
  });
478
487
  }
479
488
 
489
+ if ( !opt.transient && opt.watch !== false ) {
490
+ dispatch('watch', watchArgs);
491
+ }
492
+
480
493
  if (opt.hasManualRefresh) {
481
494
  dispatch('resource-fetch/updateManualRefreshIsLoading', false, { root: true });
482
495
  }
@@ -671,7 +684,15 @@ export default {
671
684
  return out;
672
685
  },
673
686
 
674
- load(ctx, { data, existing }) {
687
+ /**
688
+ * Add or update the given resource in the store
689
+ *
690
+ * invalidatePageCache
691
+ * - if something calls `load` then the cache no longer has a page so we invalidate it
692
+ * - however on resource create or remove this can lead to lists showing nothing... before the new page is fetched
693
+ * - for those cases avoid invaliding the page cache
694
+ */
695
+ load(ctx, { data, existing, invalidatePageCache }) {
675
696
  const { getters, commit } = ctx;
676
697
 
677
698
  let type = normalizeType(data.type);
@@ -704,7 +725,8 @@ export default {
704
725
  commit('load', {
705
726
  ctx,
706
727
  data,
707
- existing
728
+ existing,
729
+ invalidatePageCache // Avoid havePage invalidation
708
730
  });
709
731
 
710
732
  if ( type === SCHEMA ) {
@@ -50,6 +50,33 @@ export const urlFor = (state, getters) => (type, id, opt) => {
50
50
  return url;
51
51
  };
52
52
 
53
+ function resourceCount(rootGetters, getters, typeObj) {
54
+ let _typeObj = typeObj;
55
+ const { name: type, count } = _typeObj;
56
+
57
+ if (!type) {
58
+ throw new Error(`Resource type required to calc count: ${ JSON.stringify(typeObj) }`);
59
+ }
60
+
61
+ if (!count) {
62
+ const schema = getters.schemaFor(type);
63
+ const counts = getters.all(COUNT)?.[0]?.counts || {};
64
+ const count = counts[type];
65
+
66
+ // This object aligns with `Type.vue` `type`
67
+ _typeObj = {
68
+ count: count ? count.summary.count || 0 : null,
69
+ byNamespace: count ? count.namespaces : {},
70
+ revision: count ? count.revision : null,
71
+ namespaced: schema?.attributes?.namespaced
72
+ };
73
+ }
74
+
75
+ const namespaces = _typeObj?.namespaced && !rootGetters.isAllNamespaces ? Object.keys(rootGetters.activeNamespaceCache || {}) : [];
76
+
77
+ return matchingCounts(_typeObj, namespaces.length ? namespaces : null);
78
+ }
79
+
53
80
  /**
54
81
  * Find the number of resources given
55
82
  * - if the type is namespaced
@@ -374,6 +401,9 @@ export default {
374
401
  return state.types[type]?.haveNamespace || null;
375
402
  },
376
403
 
404
+ /**
405
+ * Returns (type: string ) => StorePagination
406
+ */
377
407
  havePage: (state, getters) => (type) => {
378
408
  type = getters.normalizeType(type);
379
409
 
@@ -455,30 +485,13 @@ export default {
455
485
  *
456
486
  */
457
487
  count: (state, getters, rootState, rootGetters) => (typeObj) => {
458
- let _typeObj = typeObj;
459
- const { name: type, count } = _typeObj;
488
+ const subTypes = rootGetters['type-map/optionsFor'](typeObj.name).subTypes || [];
460
489
 
461
- if (!type) {
462
- throw new Error(`Resource type required to calc count: ${ JSON.stringify(typeObj) }`);
490
+ if (subTypes.length) {
491
+ return subTypes.reduce((acc, type) => acc + resourceCount(rootGetters, getters, { name: type }), 0);
463
492
  }
464
493
 
465
- if (!count) {
466
- const schema = getters.schemaFor(type);
467
- const counts = getters.all(COUNT)?.[0]?.counts || {};
468
- const count = counts[type];
469
-
470
- // This object aligns with `Type.vue` `type`
471
- _typeObj = {
472
- count: count ? count.summary.count || 0 : null,
473
- byNamespace: count ? count.namespaces : {},
474
- revision: count ? count.revision : null,
475
- namespaced: schema?.attributes?.namespaced
476
- };
477
- }
478
-
479
- const namespaces = _typeObj?.namespaced && !rootGetters.isAllNamespaces ? Object.keys(rootGetters.activeNamespaceCache || {}) : [];
480
-
481
- return matchingCounts(_typeObj, namespaces.length ? namespaces : null);
494
+ return resourceCount(rootGetters, getters, typeObj);
482
495
  },
483
496
 
484
497
  generation: (state, getters) => (type) => {
@@ -86,8 +86,16 @@ export function createLoadArgs(ctx, dataType) {
86
86
  };
87
87
  }
88
88
 
89
+ /**
90
+ * Add or update the given resource in the store
91
+ *
92
+ * invalidatePageCache
93
+ * - if something calls `load` then the cache no longer has a page so we invalidate it
94
+ * - however on resource create or remove this can lead to lists showing nothing... before the new page is fetched
95
+ * - for those cases avoid invaliding the page cache
96
+ */
89
97
  export function load(state, {
90
- data, ctx, existing, cachedArgs
98
+ data, ctx, existing, cachedArgs, invalidatePageCache = true,
91
99
  }) {
92
100
  const { getters } = ctx;
93
101
  // Optimisation. This can run once per resource loaded.., so pass in from parent
@@ -130,8 +138,12 @@ export function load(state, {
130
138
  }
131
139
  } else {
132
140
  if (inMap) {
141
+ // In theory cached `entry` should match provided `existing`, so changes merged from `data` into `entry` should be reflected in `existing`.
142
+ // However.. there's a disconnect happening somewhere so merge data into `existing` before merging into `entry`
143
+ const latestEntry = existing && entry !== existing ? replaceResource(existing, data, getters) : data;
144
+
133
145
  // There's already an entry in the store, so merge changes into it. The list entry is a reference to the map (and vice versa)
134
- entry = replaceResource(entry, data, getters);
146
+ entry = replaceResource(entry, latestEntry, getters);
135
147
  } else {
136
148
  // There's no entry, make a new proxy
137
149
  entry = reactive(classify(ctx, data));
@@ -164,7 +176,8 @@ export function load(state, {
164
176
  }
165
177
  }
166
178
 
167
- cache.havePage = false;
179
+ // see `invalidatePageCache` description above
180
+ cache.havePage = invalidatePageCache ? false : cache.havePage;
168
181
 
169
182
  return entry;
170
183
  }
@@ -476,29 +489,60 @@ export default {
476
489
  data,
477
490
  ctx,
478
491
  pagination,
492
+ revision
479
493
  }) {
480
494
  if (!data) {
481
495
  return;
482
496
  }
497
+ // We loop over data three times in this mutator, which is bad
498
+ // However we're only dealing with pageSize worth of data and splitting out into three loops improves legibility
483
499
 
484
500
  const keyField = ctx.getters.keyFieldForType(type);
485
- const proxies = reactive(data.map((x) => classify(ctx, x)));
501
+
502
+ // Why don't we just replace the map? Because we
503
+ // 1. nav to list, subscribe to changes
504
+ // 2. nav to resource in list
505
+ // 3. update to page comes over
506
+ // 4. need to update the reference the detail list uses
507
+ const proxiesMap = {};
508
+ const proxies = reactive(data.map((x) => {
509
+ proxiesMap[x[keyField]] = true;
510
+
511
+ return classify(ctx, x);
512
+ }));
486
513
  const cache = registerType(state, type);
487
514
 
488
- clear(cache.list);
489
- cache.map.clear();
490
515
  cache.generation++;
491
516
 
517
+ // Update list
518
+ clear(cache.list);
492
519
  addObjects(cache.list, proxies);
493
520
 
521
+ // Update Map (remove stale)
522
+ cache.map.forEach((value, key) => {
523
+ if (!proxiesMap[value[keyField]]) {
524
+ cache.map.delete(key);
525
+ }
526
+ });
527
+
528
+ // Update Map (update existing / add latest)
494
529
  for ( let i = 0 ; i < proxies.length ; i++ ) {
495
- cache.map.set(proxies[i][keyField], proxies[i]);
530
+ // This could probably be merged with the first loop above
531
+ const existing = cache.map.get(proxies[i][keyField]);
532
+ const latest = proxies[i];
533
+
534
+ if (existing) {
535
+ replaceResource(existing, latest, ctx.getters);
536
+ } else {
537
+ cache.map.set(latest[keyField], latest);
538
+ }
496
539
  }
497
540
 
498
541
  // havePage is of type `StorePagination`
499
542
  cache.havePage = pagination;
500
543
  cache.haveNamespace = undefined;
501
544
  cache.haveAll = undefined;
545
+ cache.revision = revision;
502
546
 
503
547
  return proxies;
504
548
  },
@@ -1180,7 +1180,8 @@ export default class Resource {
1180
1180
  }
1181
1181
 
1182
1182
  // @TODO remove this once the API maps steve _type <-> k8s type in both directions
1183
- // Completely disconnect the object we're going to save (and mangle in cleanForSave) and `this`
1183
+ // `JSON.parse(JSON.stringify` - Completely disconnect the object we're going to send and `this`. This ensures that properties
1184
+ // removed from opt.data before sending (as part of cleanForSave) are not stripped from where they're still needed (`this`)
1184
1185
  opt.data = this.toSave() || JSON.parse(JSON.stringify(this));
1185
1186
 
1186
1187
  if (opt.data._type) {
@@ -1248,7 +1249,18 @@ export default class Resource {
1248
1249
 
1249
1250
  const res = await this.$dispatch('request', { opt, type: this.type } );
1250
1251
 
1251
- if ( res?._status === 204 ) {
1252
+ // In theory...
1253
+ // 200 - resource could have finalizer (could hang around, keep resource to show deleting state)
1254
+ // 204 - resource should be gone gone (so remove immediately)
1255
+ // However...
1256
+ // 200 - this is the only status code returned
1257
+ if ( res?._status === 200 ) {
1258
+ // Show state (probably terminating) immediately, don't wait for resource.change or debounced resource.changes update
1259
+ // It would be neater to only do this in the debounced resource.changes world, but there's no neat / complete way to do this (paginationUtils will cause dep issues if imported)
1260
+ await this.$dispatch('load', {
1261
+ data: res, existing: this, invalidatePageCache: false
1262
+ });
1263
+ } else if ( res?._status === 204 ) {
1252
1264
  // If there's no body, assume the resource was immediately deleted
1253
1265
  // and drop it from the store as if a remove event happened.
1254
1266
  await this.$dispatch('ws.resource.remove', { data: this });
@@ -1,4 +1,4 @@
1
- import { actions } from '../subscribe';
1
+ import { actions, getters } from '../subscribe';
2
2
 
3
3
  describe('steve: subscribe', () => {
4
4
  describe('actions', () => {
@@ -106,4 +106,69 @@ describe('steve: subscribe', () => {
106
106
  });
107
107
  });
108
108
  });
109
+
110
+ describe('getters', () => {
111
+ describe('nextResourceVersion', () => {
112
+ const myType = 'myType';
113
+ const myId = `myId`;
114
+
115
+ type Revision = string | number | null | undefined
116
+
117
+ const createState = (revision?: Revision, cachedList?: any[] ) => {
118
+ const typeState: any = {};
119
+
120
+ if (revision !== undefined) {
121
+ typeState.revision = revision;
122
+ }
123
+ if (cachedList !== undefined) {
124
+ typeState.list = cachedList;
125
+ } else {
126
+ typeState.list = [];
127
+ }
128
+
129
+ return { types: { [myType.toLocaleLowerCase()]: typeState } };
130
+ };
131
+
132
+ const createGetters = (revision?: Revision) => {
133
+ return {
134
+ byId: (type: any, id: any) => {
135
+ expect(type).toBe(myType.toLowerCase());
136
+ expect(id).toBe(myId);
137
+
138
+ return { metadata: { resourceVersion: revision } };
139
+ }
140
+ };
141
+ };
142
+
143
+ it.each([
144
+ ['Is String, Return String', createGetters('abc'), myId, 'abc'],
145
+ // This catches old parseInt errors
146
+ ['Is String, Return String (integer prefix)', createGetters('123-abc'), myId, '123-abc'],
147
+ // This catches old parseInt errors
148
+ ['Is String, Return String (integer postfix)', createGetters('abc-123'), myId, 'abc-123'],
149
+ ['Is 0, Return Null', createGetters(0), myId, null],
150
+ ['Is Number, Return Number', createGetters(123), myId, 123],
151
+ ['Is Missing (null), Return Null', createGetters(null), myId, null],
152
+ ['Is Missing (undefined), Return Null', createGetters(undefined), myId, null],
153
+ ])('test revision from a single resource - %s', (_, storeGetters, id, expected) => {
154
+ expect(getters.nextResourceVersion(createState(), storeGetters)(myType, id)).toBe(expected);
155
+ });
156
+
157
+ const createResource = (revision: Revision) => ({ metadata: { resourceVersion: revision } });
158
+
159
+ it.each([
160
+ ['Is String, Return String', createState('abc'), 'abc'],
161
+ ['Is String, Return String (integer prefix)', createState('123-abc'), '123-abc'],
162
+ ['Is String, Return String (integer postfix)', createState('abc-123'), 'abc-123'],
163
+ ['Is 0, Return Null (nothing in cache)', createState(0), null],
164
+ ['Is 0, Return Number (numbers in cache)', createState(0, [createResource(1), createResource(2), createResource(3)]), 3],
165
+ ['Is 0, Return Number (numbers and strings in cache)', createState(0, [createResource(1), createResource('abc-2'), createResource('3-abc')]), 1],
166
+ ['Is 0, Return Null (only strings in cache)', createState(0, [createResource('abc'), createResource('abc-2'), createResource('3-abc')]), null],
167
+ ['Is missing (undefined), Return Null ', createState(undefined), null],
168
+ ['Is missing (null), Return Null', createState(null), null],
169
+ ])('test revision from a collection - %s', (_, state, expected) => {
170
+ expect(getters.nextResourceVersion(state, {})(myType)).toBe(expected);
171
+ });
172
+ });
173
+ });
109
174
  });
@@ -58,6 +58,9 @@ export default {
58
58
  waiting = state.deferredRequests[key];
59
59
 
60
60
  if ( waiting ) {
61
+ // A matching request has already been made and is currently waiting to complete
62
+ // Avoid making another request, just wait for the original one to complete
63
+ // and return the result of the first call (see `waiting` being processed far below)
61
64
  const later = deferred();
62
65
 
63
66
  waiting.push(later);
@@ -649,7 +649,7 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStore = {
649
649
  enabled: [
650
650
  NODE, EVENT,
651
651
  WORKLOAD_TYPES.CRON_JOB, WORKLOAD_TYPES.DAEMON_SET, WORKLOAD_TYPES.DEPLOYMENT, WORKLOAD_TYPES.JOB, WORKLOAD_TYPES.STATEFUL_SET, POD,
652
- CATALOG.APP, CATALOG.CLUSTER_REPO, CATALOG.OPERATION,
652
+ CATALOG.APP, CATALOG.OPERATION,
653
653
  HPA, INGRESS, SERVICE,
654
654
  PV, CONFIG_MAP, STORAGE_CLASS, PVC, SECRET,
655
655
  WORKLOAD_TYPES.REPLICA_SET, WORKLOAD_TYPES.REPLICATION_CONTROLLER
@@ -658,18 +658,19 @@ export const PAGINATION_SETTINGS_STORE_DEFAULTS: PaginationSettingsStore = {
658
658
  }
659
659
  }
660
660
  },
661
- management: {
662
- resources: {
663
- enableAll: false,
664
- enableSome: {
665
- enabled: [
666
- { resource: CAPI.RANCHER_CLUSTER, context: ['home', 'side-bar'] },
667
- { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
668
- ],
669
- generic: false,
670
- }
671
- }
672
- }
661
+ // Disabled due to https://github.com/rancher/dashboard/issues/14493
662
+ // management: {
663
+ // resources: {
664
+ // enableAll: false,
665
+ // enableSome: {
666
+ // enabled: [
667
+ // { resource: CAPI.RANCHER_CLUSTER, context: ['home', 'side-bar'] },
668
+ // { resource: MANAGEMENT.CLUSTER, context: ['side-bar'] },
669
+ // ],
670
+ // generic: false,
671
+ // }
672
+ // }
673
+ // }
673
674
  };
674
675
 
675
676
  export default new StevePaginationUtils();