@rancher/shell 3.0.8-rc.8 → 3.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/apis/impl/apis.ts +61 -0
  2. package/apis/index.ts +40 -0
  3. package/apis/intf/modal.ts +90 -0
  4. package/apis/intf/shell.ts +36 -0
  5. package/apis/intf/slide-in.ts +98 -0
  6. package/apis/intf/system.ts +41 -0
  7. package/apis/shell/__tests__/modal.test.ts +80 -0
  8. package/apis/shell/__tests__/notifications.test.ts +71 -0
  9. package/apis/shell/__tests__/slide-in.test.ts +54 -0
  10. package/apis/shell/__tests__/system.test.ts +129 -0
  11. package/apis/shell/index.ts +38 -0
  12. package/apis/shell/modal.ts +41 -0
  13. package/apis/shell/notifications.ts +65 -0
  14. package/apis/shell/slide-in.ts +33 -0
  15. package/apis/shell/system.ts +65 -0
  16. package/apis/vue-shim.d.ts +11 -0
  17. package/assets/brand/suse/dark/rancher-logo.svg +1 -64
  18. package/assets/styles/global/_tooltip.scss +6 -1
  19. package/assets/translations/en-us.yaml +14 -1
  20. package/components/ActionMenuShell.vue +3 -1
  21. package/components/BackLink.vue +8 -0
  22. package/components/BannerGraphic.vue +1 -5
  23. package/components/BrandImage.vue +17 -6
  24. package/components/Cron/CronExpressionEditor.vue +1 -1
  25. package/components/Cron/CronExpressionEditorModal.vue +1 -1
  26. package/components/CruResource.vue +8 -1
  27. package/components/Drawer/ResourceDetailDrawer/ConfigTab.vue +1 -0
  28. package/components/Drawer/ResourceDetailDrawer/__tests__/composables.test.ts +50 -1
  29. package/components/Drawer/ResourceDetailDrawer/composables.ts +19 -0
  30. package/components/Drawer/ResourceDetailDrawer/index.vue +4 -1
  31. package/components/Drawer/ResourceDetailDrawer/types.ts +2 -1
  32. package/components/LocaleSelector.vue +2 -2
  33. package/components/ModalManager.vue +11 -1
  34. package/components/Questions/__tests__/Yaml.test.ts +1 -1
  35. package/components/Questions/__tests__/index.test.ts +159 -0
  36. package/components/RelatedResources.vue +5 -0
  37. package/components/Resource/Detail/Metadata/Annotations/index.vue +2 -2
  38. package/components/Resource/Detail/Metadata/Labels/index.vue +2 -2
  39. package/components/Resource/Detail/Metadata/index.vue +3 -3
  40. package/components/Resource/Detail/ResourcePopover/index.vue +5 -1
  41. package/components/Resource/Detail/composables.ts +2 -2
  42. package/components/ResourceDetail/Masthead/latest.vue +23 -21
  43. package/components/ResourceDetail/index.vue +3 -0
  44. package/components/ResourceTable.vue +54 -21
  45. package/components/SlideInPanelManager.vue +16 -11
  46. package/components/SortableTable/THead.vue +2 -1
  47. package/components/SortableTable/index.vue +20 -2
  48. package/components/Tabbed/__tests__/index.test.ts +86 -0
  49. package/components/Tabbed/index.vue +37 -2
  50. package/components/__tests__/NamespaceFilter.test.ts +49 -0
  51. package/components/auth/SelectPrincipal.vue +28 -6
  52. package/components/auth/__tests__/SelectPrincipal.test.ts +119 -0
  53. package/components/auth/login/ldap.vue +3 -3
  54. package/components/fleet/FleetSecretSelector.vue +1 -1
  55. package/components/form/KeyValue.vue +1 -1
  56. package/components/form/NameNsDescription.vue +1 -1
  57. package/components/form/NodeScheduling.vue +2 -2
  58. package/components/form/ResourceTabs/composable.ts +2 -2
  59. package/components/form/ResourceTabs/index.vue +0 -2
  60. package/components/form/__tests__/NameNsDescription.test.ts +42 -0
  61. package/components/formatter/InternalExternalIP.vue +4 -1
  62. package/components/formatter/LinkName.vue +5 -0
  63. package/components/formatter/__tests__/InternalExternalIP.test.ts +1 -1
  64. package/components/nav/Group.vue +25 -7
  65. package/components/nav/Header.vue +1 -1
  66. package/components/nav/NamespaceFilter.vue +1 -0
  67. package/components/nav/Type.vue +17 -6
  68. package/components/nav/WindowManager/panels/TabBodyContainer.vue +1 -1
  69. package/components/nav/__tests__/Type.test.ts +59 -0
  70. package/components/templates/standalone.vue +1 -1
  71. package/composables/cruResource.ts +27 -0
  72. package/composables/focusTrap.ts +3 -1
  73. package/composables/resourceDetail.ts +15 -0
  74. package/composables/useI18n.ts +10 -1
  75. package/composables/useLabeledFormElement.ts +3 -4
  76. package/config/__test__/uiplugins.test.ts +309 -0
  77. package/config/labels-annotations.js +1 -0
  78. package/config/product/explorer.js +3 -1
  79. package/config/product/fleet.js +1 -1
  80. package/config/router/navigation-guards/clusters.js +3 -3
  81. package/config/router/navigation-guards/products.js +1 -1
  82. package/config/router/routes.js +7 -7
  83. package/config/types.js +7 -0
  84. package/config/uiplugins.js +46 -2
  85. package/core/__tests__/extension-manager-impl.test.js +437 -0
  86. package/core/extension-manager-impl.js +21 -25
  87. package/core/plugin-helpers.ts +2 -2
  88. package/core/plugin.ts +9 -1
  89. package/core/plugins-loader.js +2 -2
  90. package/core/types-provisioning.ts +5 -1
  91. package/core/types.ts +35 -0
  92. package/detail/provisioning.cattle.io.cluster.vue +9 -6
  93. package/dialog/DeveloperLoadExtensionDialog.vue +13 -4
  94. package/dialog/MoveNamespaceDialog.vue +20 -4
  95. package/dialog/RollbackWorkloadDialog.vue +2 -5
  96. package/dialog/SearchDialog.vue +1 -0
  97. package/dialog/__tests__/MoveNamespaceDialog.test.ts +249 -0
  98. package/directives/__tests__/clean-tooltip.test.ts +298 -0
  99. package/directives/clean-tooltip.ts +234 -0
  100. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +2 -2
  101. package/edit/__tests__/fleet.cattle.io.helmop.test.ts +100 -3
  102. package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -0
  103. package/edit/configmap.vue +1 -0
  104. package/edit/constraints.gatekeeper.sh.constraint/index.vue +1 -0
  105. package/edit/fleet.cattle.io.helmop.vue +11 -6
  106. package/edit/helm.cattle.io.projecthelmchart.vue +1 -0
  107. package/edit/k8s.cni.cncf.io.networkattachmentdefinition.vue +1 -0
  108. package/edit/logging-flow/index.vue +1 -0
  109. package/edit/logging.banzaicloud.io.output/index.vue +1 -0
  110. package/edit/management.cattle.io.fleetworkspace.vue +1 -1
  111. package/edit/management.cattle.io.project.vue +1 -0
  112. package/edit/monitoring.coreos.com.alertmanagerconfig/index.vue +4 -1
  113. package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +2 -1
  114. package/edit/monitoring.coreos.com.prometheusrule/index.vue +1 -0
  115. package/edit/monitoring.coreos.com.receiver/index.vue +2 -1
  116. package/edit/monitoring.coreos.com.route.vue +1 -1
  117. package/edit/namespace.vue +1 -0
  118. package/edit/networking.istio.io.destinationrule/index.vue +1 -0
  119. package/edit/networking.k8s.io.ingress/index.vue +1 -0
  120. package/edit/networking.k8s.io.networkpolicy/PolicyRules.vue +1 -0
  121. package/edit/networking.k8s.io.networkpolicy/index.vue +1 -0
  122. package/edit/node.vue +1 -0
  123. package/edit/persistentvolume/index.vue +27 -22
  124. package/edit/persistentvolume/plugins/awsElasticBlockStore.vue +13 -14
  125. package/edit/persistentvolume/plugins/azureDisk.vue +49 -48
  126. package/edit/persistentvolume/plugins/azureFile.vue +15 -14
  127. package/edit/persistentvolume/plugins/cephfs.vue +15 -14
  128. package/edit/persistentvolume/plugins/cinder.vue +15 -14
  129. package/edit/persistentvolume/plugins/csi.vue +18 -16
  130. package/edit/persistentvolume/plugins/fc.vue +13 -14
  131. package/edit/persistentvolume/plugins/flexVolume.vue +15 -14
  132. package/edit/persistentvolume/plugins/flocker.vue +1 -3
  133. package/edit/persistentvolume/plugins/gcePersistentDisk.vue +13 -14
  134. package/edit/persistentvolume/plugins/glusterfs.vue +15 -14
  135. package/edit/persistentvolume/plugins/hostPath.vue +40 -39
  136. package/edit/persistentvolume/plugins/iscsi.vue +13 -14
  137. package/edit/persistentvolume/plugins/local.vue +1 -3
  138. package/edit/persistentvolume/plugins/longhorn.vue +23 -22
  139. package/edit/persistentvolume/plugins/nfs.vue +15 -14
  140. package/edit/persistentvolume/plugins/photonPersistentDisk.vue +1 -14
  141. package/edit/persistentvolume/plugins/portworxVolume.vue +15 -14
  142. package/edit/persistentvolume/plugins/quobyte.vue +15 -14
  143. package/edit/persistentvolume/plugins/rbd.vue +15 -14
  144. package/edit/persistentvolume/plugins/scaleIO.vue +15 -14
  145. package/edit/persistentvolume/plugins/storageos.vue +15 -14
  146. package/edit/persistentvolume/plugins/vsphereVolume.vue +1 -3
  147. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +21 -21
  148. package/edit/provisioning.cattle.io.cluster/index.vue +5 -5
  149. package/edit/provisioning.cattle.io.cluster/rke2.vue +9 -8
  150. package/edit/resources.cattle.io.restore.vue +1 -1
  151. package/edit/secret/index.vue +1 -1
  152. package/edit/service.vue +1 -0
  153. package/edit/serviceaccount.vue +1 -0
  154. package/edit/storage.k8s.io.storageclass/index.vue +1 -0
  155. package/edit/workload/Job.vue +2 -2
  156. package/edit/workload/index.vue +2 -1
  157. package/edit/workload/mixins/workload.js +1 -1
  158. package/initialize/App.vue +4 -4
  159. package/initialize/install-plugins.js +19 -5
  160. package/machine-config/azure.vue +1 -1
  161. package/machine-config/components/GCEImage.vue +1 -1
  162. package/mixins/__tests__/brand.spec.ts +2 -2
  163. package/mixins/brand.js +1 -7
  164. package/mixins/create-edit-view/index.js +5 -0
  165. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +128 -5
  166. package/models/chart.js +70 -74
  167. package/models/management.cattle.io.cluster.js +21 -3
  168. package/models/provisioning.cattle.io.cluster.js +31 -11
  169. package/package.json +11 -10
  170. package/pages/auth/login.vue +4 -6
  171. package/pages/auth/setup.vue +1 -1
  172. package/pages/auth/verify.vue +3 -3
  173. package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +135 -0
  174. package/pages/c/_cluster/apps/charts/chart.vue +33 -15
  175. package/pages/c/_cluster/apps/charts/index.vue +122 -24
  176. package/pages/c/_cluster/apps/charts/install.vue +33 -0
  177. package/pages/c/_cluster/explorer/__tests__/index.test.ts +1 -1
  178. package/pages/c/_cluster/explorer/index.vue +8 -6
  179. package/pages/c/_cluster/fleet/index.vue +4 -7
  180. package/pages/c/_cluster/manager/hostedprovider/index.vue +12 -6
  181. package/pages/c/_cluster/settings/brand.vue +1 -1
  182. package/pages/c/_cluster/settings/index.vue +5 -0
  183. package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +7 -0
  184. package/pages/c/_cluster/uiplugins/catalogs.vue +147 -0
  185. package/pages/c/_cluster/uiplugins/index.vue +126 -184
  186. package/pkg/auto-import.js +3 -3
  187. package/pkg/dynamic-importer.lib.js +1 -1
  188. package/pkg/import.js +1 -1
  189. package/plugins/__tests__/mutations.tests.ts +179 -0
  190. package/plugins/dashboard-client-init.js +3 -0
  191. package/plugins/dashboard-store/getters.js +19 -2
  192. package/plugins/dashboard-store/model-loader.js +1 -1
  193. package/plugins/dashboard-store/mutations.js +23 -2
  194. package/plugins/dashboard-store/resource-class.js +11 -5
  195. package/plugins/i18n.js +8 -0
  196. package/plugins/plugin.js +2 -2
  197. package/plugins/steve/__tests__/steve-pagination-utils.test.ts +506 -0
  198. package/plugins/steve/steve-class.js +1 -1
  199. package/plugins/steve/steve-pagination-utils.ts +131 -47
  200. package/rancher-components/Form/Checkbox/Checkbox.vue +1 -1
  201. package/rancher-components/Form/LabeledInput/LabeledInput.vue +1 -1
  202. package/rancher-components/Pill/RcStatusBadge/RcStatusBadge.vue +6 -42
  203. package/rancher-components/Pill/RcStatusBadge/index.ts +0 -1
  204. package/rancher-components/Pill/RcStatusBadge/types.ts +1 -1
  205. package/rancher-components/Pill/RcStatusIndicator/RcStatusIndicator.vue +5 -28
  206. package/rancher-components/Pill/RcStatusIndicator/types.ts +2 -1
  207. package/rancher-components/Pill/types.ts +0 -1
  208. package/rancher-components/RcDropdown/useDropdownContext.ts +2 -4
  209. package/rancher-components/RcIcon/RcIcon.test.ts +51 -0
  210. package/rancher-components/RcIcon/RcIcon.vue +46 -0
  211. package/rancher-components/RcIcon/index.ts +1 -0
  212. package/rancher-components/RcIcon/types.ts +160 -0
  213. package/rancher-components/RcItemCard/RcItemCard.vue +1 -1
  214. package/rancher-components/utils/status.test.ts +67 -0
  215. package/rancher-components/utils/status.ts +77 -0
  216. package/scripts/publish-shell.sh +25 -0
  217. package/scripts/typegen.sh +1 -0
  218. package/store/__tests__/catalog.test.ts +1 -1
  219. package/store/__tests__/type-map.test.ts +164 -2
  220. package/store/action-menu.js +8 -0
  221. package/store/auth.js +25 -13
  222. package/store/catalog.js +6 -0
  223. package/store/i18n.js +3 -3
  224. package/store/index.js +8 -6
  225. package/store/notifications.ts +2 -0
  226. package/store/prefs.js +6 -7
  227. package/store/type-map.js +17 -7
  228. package/store/wm.ts +4 -4
  229. package/types/internal-api/shell/modal.d.ts +6 -6
  230. package/types/notifications/index.ts +126 -15
  231. package/types/rancher/index.d.ts +9 -0
  232. package/types/shell/index.d.ts +54 -3
  233. package/types/store/__tests__/pagination.types.spec.ts +137 -0
  234. package/types/store/pagination.types.ts +157 -9
  235. package/types/vue-shim.d.ts +5 -4
  236. package/utils/__tests__/provider.test.ts +98 -0
  237. package/utils/__tests__/router.test.js +238 -0
  238. package/utils/__tests__/selector-typed.test.ts +263 -0
  239. package/utils/cluster.js +4 -1
  240. package/utils/color.js +1 -1
  241. package/utils/dynamic-content/__tests__/info.test.ts +6 -0
  242. package/utils/dynamic-content/info.ts +43 -0
  243. package/utils/favicon.js +4 -4
  244. package/utils/fleet.ts +8 -1
  245. package/utils/pagination-utils.ts +2 -2
  246. package/utils/pagination-wrapper.ts +1 -1
  247. package/utils/provider.ts +14 -0
  248. package/utils/router.js +50 -0
  249. package/utils/selector-typed.ts +6 -2
  250. package/utils/unit-tests/pagination-utils.spec.ts +8 -8
  251. package/vue.config.js +3 -3
  252. package/composables/useExtensionManager.ts +0 -17
  253. package/core/plugins.js +0 -38
  254. package/directives/clean-tooltip.js +0 -32
  255. package/plugins/internal-api/index.ts +0 -37
  256. package/plugins/internal-api/shared/base-api.ts +0 -13
  257. package/plugins/internal-api/shell/shell.api.ts +0 -108
  258. package/plugins/nuxt-client-init.js +0 -3
  259. package/types/internal-api/shell/growl.d.ts +0 -25
  260. package/types/internal-api/shell/slideIn.d.ts +0 -15
@@ -14,10 +14,8 @@ import ActionMenu from '@shell/components/ActionMenuShell';
14
14
  import Tabbed from '@shell/components/Tabbed/index.vue';
15
15
  import Tab from '@shell/components/Tabbed/Tab.vue';
16
16
  import IconMessage from '@shell/components/IconMessage.vue';
17
- import { BadgeState } from '@components/BadgeState';
18
17
  import PluginInfoPanel from './PluginInfoPanel.vue';
19
18
  import SetupUIPlugins from './SetupUIPlugins.vue';
20
- import CatalogList from './CatalogList/index.vue';
21
19
  import Banner from '@components/Banner/Banner.vue';
22
20
  import {
23
21
  isUIPlugin,
@@ -48,9 +46,7 @@ const TABS_VALUES = {
48
46
  export default {
49
47
  components: {
50
48
  ActionMenu,
51
- BadgeState,
52
49
  IconMessage,
53
- CatalogList,
54
50
  Banner,
55
51
  PluginInfoPanel,
56
52
  Tab,
@@ -79,8 +75,7 @@ export default {
79
75
  hasFeatureFlag: true,
80
76
  defaultIcon: require('~shell/assets/images/generic-plugin.svg'),
81
77
  reloadRequired: false,
82
- rancherVersion: null,
83
- showCatalogList: false
78
+ rancherVersion: null
84
79
  };
85
80
  },
86
81
 
@@ -175,14 +170,11 @@ export default {
175
170
  enabled: true
176
171
  });
177
172
 
178
- // Only show Manage Extension Catalogs when on main charts view
179
- if (!this.showCatalogList) {
180
- menuActions.push({
181
- action: 'manageExtensionView',
182
- label: this.t('plugins.manageCatalog.label'),
183
- enabled: true
184
- });
185
- }
173
+ menuActions.push({
174
+ action: 'manageExtensionView',
175
+ label: this.t('plugins.manageCatalog.label'),
176
+ enabled: true
177
+ });
186
178
 
187
179
  // Only show Developer Load action if the user has this enabled in preferences
188
180
  if (this.pluginDeveloper) {
@@ -204,7 +196,7 @@ export default {
204
196
  switch (this.activeTab) {
205
197
  case TABS_VALUES.INSTALLED:
206
198
  // We never show built-in extensions as installed - installed are just the ones the user has installed
207
- return all.filter((p) => !p.builtin && (!!p.installed || !!p.installing) && p.installableVersions?.length > 0);
199
+ return all.filter((p) => !p.builtin && (!!p.installed || !!p.installing));
208
200
  case TABS_VALUES.AVAILABLE:
209
201
  return all.filter((p) => !p.installed);
210
202
  case TABS_VALUES.BUILTIN:
@@ -284,6 +276,7 @@ export default {
284
276
  const latestNotCompatible = item.versions.find((version) => !version.isVersionCompatible);
285
277
 
286
278
  if (latestCompatible) {
279
+ item.primeOnly = latestCompatible?.annotations?.[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true';
287
280
  item.experimental = latestCompatible?.annotations?.[CATALOG_ANNOTATIONS.EXPERIMENTAL] === 'true';
288
281
  item.certified = latestCompatible?.annotations?.[CATALOG_ANNOTATIONS.CERTIFIED] === CATALOG_ANNOTATIONS._RANCHER;
289
282
 
@@ -292,6 +285,7 @@ export default {
292
285
  item.icon = latestCompatible.icon;
293
286
  item.created = latestCompatible.created;
294
287
  } else {
288
+ item.primeOnly = uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.PRIME_ONLY, 'true');
295
289
  item.experimental = uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.EXPERIMENTAL, 'true');
296
290
  item.certified = uiPluginHasAnnotation(chart, CATALOG_ANNOTATIONS.CERTIFIED, CATALOG_ANNOTATIONS._RANCHER);
297
291
 
@@ -349,6 +343,7 @@ export default {
349
343
  installed: true,
350
344
  installedVersion: p.metadata?.version,
351
345
  builtin: !!p.builtin,
346
+ primeOnly: rancher?.annotations?.[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true',
352
347
  experimental: rancher?.annotations?.[CATALOG_ANNOTATIONS.EXPERIMENTAL] === 'true',
353
348
  certified: rancher?.annotations?.[CATALOG_ANNOTATIONS.CERTIFIED] === CATALOG_ANNOTATIONS._RANCHER
354
349
  };
@@ -381,6 +376,7 @@ export default {
381
376
  const installedVersion = (chart.installableVersions || []).find((v) => (v.appVersion ?? v.version) === p.version);
382
377
 
383
378
  if (installedVersion) {
379
+ chart.primeOnly = installedVersion?.annotations?.[CATALOG_ANNOTATIONS.PRIME_ONLY] === 'true';
384
380
  chart.experimental = installedVersion?.annotations?.[CATALOG_ANNOTATIONS.EXPERIMENTAL] === 'true';
385
381
  chart.certified = installedVersion?.annotations?.[CATALOG_ANNOTATIONS.CERTIFIED] === CATALOG_ANNOTATIONS._RANCHER;
386
382
  }
@@ -397,6 +393,7 @@ export default {
397
393
  versions: [],
398
394
  displayVersion: p.version,
399
395
  displayVersionLabel: p.version || '-',
396
+ isDeveloper: p.isDeveloper,
400
397
  installed: true,
401
398
  installing: false,
402
399
  builtin: false,
@@ -593,37 +590,6 @@ export default {
593
590
  });
594
591
  },
595
592
 
596
- showCatalogLoadDialog() {
597
- this.$store.dispatch('management/promptModal', {
598
- component: 'ExtensionCatalogInstallDialog',
599
- returnFocusSelector: '[data-testid="extensions-catalog-load-dialog"]',
600
- componentProps: {
601
- refresh: () => {
602
- this.reloadRequired = true;
603
- },
604
- closed: (res) => {
605
- this.didInstall(res);
606
- }
607
- }
608
- });
609
- },
610
-
611
- showCatalogUninstallDialog(ev) {
612
- this.$store.dispatch('management/promptModal', {
613
- component: 'ExtensionCatalogUninstallDialog',
614
- returnFocusSelector: '[data-testid="extensions-catalog-load-dialog"]',
615
- componentProps: {
616
- catalog: ev,
617
- refresh: () => {
618
- this.reloadRequired = true;
619
- },
620
- closed: (res) => {
621
- this.didUninstall(res);
622
- }
623
- }
624
- });
625
- },
626
-
627
593
  showInstallDialog(plugin, action, ev, initialVersion = null) {
628
594
  ev?.target?.blur();
629
595
  ev?.preventDefault?.();
@@ -731,7 +697,10 @@ export default {
731
697
  },
732
698
 
733
699
  manageExtensionView() {
734
- this.showCatalogList = !this.showCatalogList;
700
+ this.$router.push({
701
+ name: 'c-cluster-uiplugins-catalogs',
702
+ params: { cluster: this.$route.params.cluster }
703
+ });
735
704
  },
736
705
 
737
706
  updateAddReposSetting() {
@@ -844,11 +813,20 @@ export default {
844
813
  getFooterItems(plugin) {
845
814
  const labels = [];
846
815
 
816
+ // "developer load" tag
817
+ if (plugin.isDeveloper) {
818
+ labels.push(this.t('plugins.labels.isDeveloper'));
819
+ }
820
+
821
+ if (plugin.primeOnly) {
822
+ labels.push(this.t('plugins.labels.primeOnly'));
823
+ }
824
+
847
825
  if (plugin.builtin) {
848
826
  labels.push(this.t('plugins.labels.builtin'));
849
827
  }
850
828
 
851
- if (!plugin.builtin && !plugin.certified) {
829
+ if (!plugin.builtin && !plugin.certified && !plugin.isDeveloper) {
852
830
  labels.push(this.t('plugins.labels.third-party'));
853
831
  }
854
832
 
@@ -908,39 +886,12 @@ export default {
908
886
  <template>
909
887
  <div id="extensions-main-page">
910
888
  <div class="plugin-header">
911
- <!-- catalog view header -->
912
- <template v-if="showCatalogList">
913
- <div class="catalog-title">
914
- <h2
915
- class="mb-0 mr-10"
916
- data-testid="extensions-catalog-title"
917
- >
918
- <a
919
- class="link"
920
- role="link"
921
- tabindex="0"
922
- :aria-label="t('plugins.manageCatalog.title')"
923
- @click="manageExtensionView()"
924
- >
925
- {{ t('plugins.manageCatalog.title') }}:
926
- </a>
927
- <t k="plugins.manageCatalog.subtitle" />
928
- </h2>
929
- <BadgeState
930
- color="bg-warning"
931
- :label="t('generic.experimental')"
932
- class="badge"
933
- />
934
- </div>
935
- </template>
936
889
  <!-- normal extensions view header -->
937
- <template v-else>
938
- <h2 data-testid="extensions-page-title">
939
- <TabTitle breadcrumb="vendor-only">
940
- {{ t('plugins.title') }}
941
- </TabTitle>
942
- </h2>
943
- </template>
890
+ <h2 data-testid="extensions-page-title">
891
+ <TabTitle breadcrumb="vendor-only">
892
+ {{ t('plugins.title') }}
893
+ </TabTitle>
894
+ </h2>
944
895
  <div class="actions-container">
945
896
  <!-- extensions reload toast/notification -->
946
897
  <div
@@ -1005,112 +956,103 @@ export default {
1005
956
  />
1006
957
  </div>
1007
958
  <div v-else>
1008
- <!-- Extension Catalog list view -->
1009
- <template v-if="showCatalogList">
1010
- <CatalogList
1011
- @showCatalogLoadDialog="showCatalogLoadDialog"
1012
- @showCatalogUninstallDialog="showCatalogUninstallDialog($event)"
1013
- />
1014
- </template>
1015
- <template v-else>
1016
- <Banner
1017
- v-if="!loading && showAddReposBanner"
1018
- color="warning"
1019
- class="add-repos-banner mb-20"
1020
- data-testid="extensions-new-repos-banner"
959
+ <Banner
960
+ v-if="!loading && showAddReposBanner"
961
+ color="warning"
962
+ class="add-repos-banner mb-20"
963
+ data-testid="extensions-new-repos-banner"
964
+ >
965
+ <span>{{ t('plugins.addRepos.banner', {}, true) }}</span>
966
+ <button
967
+ class="ml-10 btn btn-sm role-primary"
968
+ data-testid="extensions-new-repos-banner-action-btn"
969
+ role="button"
970
+ :aria-label="t('plugins.addRepos.bannerBtn')"
971
+ @click="showAddExtensionReposDialog()"
1021
972
  >
1022
- <span>{{ t('plugins.addRepos.banner', {}, true) }}</span>
1023
- <button
1024
- class="ml-10 btn btn-sm role-primary"
1025
- data-testid="extensions-new-repos-banner-action-btn"
1026
- role="button"
1027
- :aria-label="t('plugins.addRepos.bannerBtn')"
1028
- @click="showAddExtensionReposDialog()"
973
+ {{ t('plugins.addRepos.bannerBtn') }}
974
+ </button>
975
+ </Banner>
976
+
977
+ <Tabbed
978
+ v-if="!loading"
979
+ ref="tabs"
980
+ :tabs-only="true"
981
+ data-testid="extension-tabs"
982
+ @changed="tabChanged"
983
+ >
984
+ <Tab
985
+ v-if="installed.length"
986
+ :name="TABS_VALUES.INSTALLED"
987
+ data-testid="extension-tab-installed"
988
+ label-key="plugins.tabs.installed"
989
+ :badge="installed.length"
990
+ :weight="20"
991
+ />
992
+ <Tab
993
+ :name="TABS_VALUES.AVAILABLE"
994
+ data-testid="extension-tab-available"
995
+ label-key="plugins.tabs.available"
996
+ :weight="19"
997
+ />
998
+ <Tab
999
+ v-if="pluginDeveloper"
1000
+ :name="TABS_VALUES.BUILTIN"
1001
+ label-key="plugins.tabs.builtin"
1002
+ :weight="17"
1003
+ />
1004
+ </Tabbed>
1005
+ <div
1006
+ v-if="loading"
1007
+ class="data-loading"
1008
+ >
1009
+ <i class="icon-spin icon icon-spinner" />
1010
+ <t
1011
+ k="generic.loading"
1012
+ :raw="true"
1013
+ />
1014
+ </div>
1015
+ <div
1016
+ v-else
1017
+ class="plugin-cards"
1018
+ :class="{'v-margin': !list.length}"
1019
+ >
1020
+ <IconMessage
1021
+ v-if="list.length === 0"
1022
+ :vertical="true"
1023
+ :subtle="true"
1024
+ icon="icon-extension"
1025
+ class="mmt-9"
1026
+ :message="emptyMessage"
1027
+ />
1028
+ <template v-else>
1029
+ <rc-item-card
1030
+ v-for="card in pluginCards"
1031
+ :id="card.id"
1032
+ :key="card.id"
1033
+ :class="{ 'single-card': pluginCards.length === 1 }"
1034
+ :header="card.header"
1035
+ :image="card.image"
1036
+ :content="card.content"
1037
+ :actions="card.actions"
1038
+ :clickable="true"
1039
+ @card-click="showPluginDetail(card.plugin)"
1040
+ @uninstall="({event}) => showUninstallDialog(card.plugin, event)"
1041
+ @upgrade="({event}) => showInstallDialog(card.plugin, 'upgrade', event)"
1042
+ @downgrade="({event}) => showInstallDialog(card.plugin, 'downgrade', event)"
1043
+ @install="({event}) => showInstallDialog(card.plugin, 'install', event)"
1029
1044
  >
1030
- {{ t('plugins.addRepos.bannerBtn') }}
1031
- </button>
1032
- </Banner>
1033
-
1034
- <Tabbed
1035
- v-if="!loading"
1036
- ref="tabs"
1037
- :tabs-only="true"
1038
- data-testid="extension-tabs"
1039
- @changed="tabChanged"
1040
- >
1041
- <Tab
1042
- v-if="installed.length"
1043
- :name="TABS_VALUES.INSTALLED"
1044
- data-testid="extension-tab-installed"
1045
- label-key="plugins.tabs.installed"
1046
- :badge="installed.length"
1047
- :weight="20"
1048
- />
1049
- <Tab
1050
- :name="TABS_VALUES.AVAILABLE"
1051
- data-testid="extension-tab-available"
1052
- label-key="plugins.tabs.available"
1053
- :weight="19"
1054
- />
1055
- <Tab
1056
- v-if="pluginDeveloper"
1057
- :name="TABS_VALUES.BUILTIN"
1058
- label-key="plugins.tabs.builtin"
1059
- :weight="17"
1060
- />
1061
- </Tabbed>
1062
- <div
1063
- v-if="loading"
1064
- class="data-loading"
1065
- >
1066
- <i class="icon-spin icon icon-spinner" />
1067
- <t
1068
- k="generic.loading"
1069
- :raw="true"
1070
- />
1071
- </div>
1072
- <div
1073
- v-else
1074
- class="plugin-cards"
1075
- :class="{'v-margin': !list.length}"
1076
- >
1077
- <IconMessage
1078
- v-if="list.length === 0"
1079
- :vertical="true"
1080
- :subtle="true"
1081
- icon="icon-extension"
1082
- class="mmt-9"
1083
- :message="emptyMessage"
1084
- />
1085
- <template v-else>
1086
- <rc-item-card
1087
- v-for="card in pluginCards"
1088
- :id="card.id"
1089
- :key="card.id"
1090
- :class="{ 'single-card': pluginCards.length === 1 }"
1091
- :header="card.header"
1092
- :image="card.image"
1093
- :content="card.content"
1094
- :actions="card.actions"
1095
- :clickable="true"
1096
- @card-click="showPluginDetail(card.plugin)"
1097
- @uninstall="({event}) => showUninstallDialog(card.plugin, event)"
1098
- @upgrade="({event}) => showInstallDialog(card.plugin, 'upgrade', event)"
1099
- @downgrade="({event}) => showInstallDialog(card.plugin, 'downgrade', event)"
1100
- @install="({event}) => showInstallDialog(card.plugin, 'install', event)"
1101
- >
1102
- <template #item-card-sub-header>
1103
- <AppChartCardSubHeader
1104
- :items="card.subHeaderItems"
1105
- />
1106
- </template>
1107
- <template #item-card-footer>
1108
- <AppChartCardFooter :items="card.footerItems" />
1109
- </template>
1110
- </rc-item-card>
1111
- </template>
1112
- </div>
1113
- </template>
1045
+ <template #item-card-sub-header>
1046
+ <AppChartCardSubHeader
1047
+ :items="card.subHeaderItems"
1048
+ />
1049
+ </template>
1050
+ <template #item-card-footer>
1051
+ <AppChartCardFooter :items="card.footerItems" />
1052
+ </template>
1053
+ </rc-item-card>
1054
+ </template>
1055
+ </div>
1114
1056
  </div>
1115
1057
  </div>
1116
1058
  </template>
@@ -15,7 +15,7 @@ function registerFile(file, type, pkg, f) {
15
15
  const importType = (f === 'models') ? 'require' : 'import';
16
16
  const chunkName = (f === 'l10n') ? '' : `/* webpackChunkName: "${ f }" */`;
17
17
 
18
- return ` $plugin.register('${ f }', '${ type }', () => ${ importType }(${ chunkName }'${ pkg }/${ f }/${ file }'));\n`;
18
+ return ` $extension.register('${ f }', '${ type }', () => ${ importType }(${ chunkName }'${ pkg }/${ f }/${ file }'));\n`;
19
19
  }
20
20
 
21
21
  function register(file, pkg, f) {
@@ -29,7 +29,7 @@ function register(file, pkg, f) {
29
29
  // This ensures that the webpackChunkName is respected (require.context does not support this) - so when build as a library
30
30
  // the code splitting will be respected
31
31
  function generateTypeImport(pkg, dir) {
32
- let content = 'export function importTypes($plugin) { \n';
32
+ let content = 'export function importTypes($extension) { \n';
33
33
 
34
34
  // Auto-import if the folder exists
35
35
  contextFolders.forEach((f) => {
@@ -77,7 +77,7 @@ function generateTypeImport(pkg, dir) {
77
77
  // and then restart the dev server for it to be picked up.
78
78
  function generateDynamicTypeImport(pkg, dir) {
79
79
  const template = fs.readFileSync(path.join(__dirname, 'import.js'), { encoding: 'utf8' });
80
- let content = 'export function importTypes($plugin) { \n';
80
+ let content = 'export function importTypes($extension) { \n';
81
81
 
82
82
  // Auto-import if the folder exists
83
83
  contextFolders.forEach((f) => {
@@ -41,7 +41,7 @@ export function listProducts() {
41
41
  return [];
42
42
  }
43
43
 
44
- export function loadProduct(name, $plugin) {
44
+ export function loadProduct(name, $extension) {
45
45
  return () => undefined;
46
46
  }
47
47
 
package/pkg/import.js CHANGED
@@ -6,5 +6,5 @@ _NAME.forEach((f) => {
6
6
 
7
7
  name = name.substr(0, ext);
8
8
 
9
- $plugin.register('DIR', name, () => REQUIRE(CHUNK`BASE/DIR/${ name }EXT`)); // eslint-disable-line no-undef
9
+ $extension.register('DIR', name, () => REQUIRE(CHUNK`BASE/DIR/${ name }EXT`)); // eslint-disable-line no-undef
10
10
  });
@@ -0,0 +1,179 @@
1
+ import mutations from '@shell/plugins/dashboard-store/mutations';
2
+ import getters from '@shell/plugins/steve/getters';
3
+ import { StorePagination } from '@shell/types/store/pagination.types';
4
+ import { VuexStore } from '@shell/types/store/vuex';
5
+ import Pod from '@shell/models/pod';
6
+
7
+ describe('mutations', () => {
8
+ jest.mock('vue', () => ({ reactive: jest.fn((arr) => arr) }));
9
+
10
+ // Import the function we are testing
11
+ const { loadPage } = mutations;
12
+
13
+ let state: any;
14
+ let ctx: Partial<VuexStore>;
15
+ let resourceType: string;
16
+ let pagination: Partial<StorePagination>;
17
+ let revision: string;
18
+
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+
22
+ state = { types: {} };
23
+ ctx = {
24
+ getters: {
25
+ keyFieldForType: jest.fn(() => 'id'),
26
+ cleanResource: getters.cleanResource()
27
+ }
28
+ };
29
+ resourceType = 'pod';
30
+ pagination = { request: undefined, result: undefined };
31
+ revision = 'abc-123';
32
+ ctx.getters.classify = () => Pod;
33
+ });
34
+
35
+ describe('loadPage', () => {
36
+ it('should perform an initial load into an empty cache', () => {
37
+ const data = [
38
+ {
39
+ id: 'a', name: 'pod-a', type: resourceType
40
+ },
41
+ {
42
+ id: 'b', name: 'pod-b', type: resourceType
43
+ }
44
+ ];
45
+
46
+ loadPage(state, {
47
+ type: resourceType, data, ctx, pagination, revision
48
+ });
49
+
50
+ // Get the cache created by the *real* 'registerType'
51
+ const cache = state.types[resourceType];
52
+
53
+ // Check cache metadata
54
+ expect(cache.generation).toBe(1);
55
+ expect(cache.havePage).toBe(pagination);
56
+ expect(cache.revision).toBe(revision);
57
+
58
+ // Check 'list' (should contain classified proxies)
59
+ expect(cache.list).toHaveLength(2);
60
+ expect(cache.list[0]).toStrictEqual(expect.objectContaining({ id: 'a' }));
61
+ expect(cache.list[1]).toStrictEqual(expect.objectContaining({ id: 'b' }));
62
+
63
+ // Check 'map'
64
+ expect(cache.map.size).toBe(2);
65
+ expect(cache.map.get('a')).toBe(cache.list[0]); // Map and list must reference the *same* objects
66
+ expect(cache.map.get('b')).toBe(cache.list[1]);
67
+ });
68
+
69
+ it('should update existing resources in-place (tests "replaceResource" effect)', () => {
70
+ const v1Resource = new Pod({ id: 'a', metadata: { a: 'v1-a' } }, ctx);
71
+
72
+ state.types[resourceType] = {
73
+ list: [v1Resource],
74
+ map: new Map([['a', v1Resource]]),
75
+ generation: 1,
76
+ };
77
+
78
+ // Keep a reference to the *original list array* and *original resource object*
79
+ const originalListRef = state.types[resourceType].list;
80
+ const originalResourceRef = v1Resource;
81
+
82
+ const newData = [
83
+ { id: 'a', metadata: { a: 'v2-a' } }, // This is the update
84
+ ];
85
+ const newRevision = 'rev-2';
86
+
87
+ loadPage(state, {
88
+ type: resourceType, data: newData, ctx, pagination, revision: newRevision
89
+ });
90
+
91
+ const cache = state.types[resourceType];
92
+
93
+ expect(cache.list).toBe(originalListRef);
94
+ expect(cache.list).toHaveLength(1);
95
+
96
+ expect(cache.list[0]).toBe(originalResourceRef);
97
+
98
+ expect(originalResourceRef.metadata.a).toBe('v2-a');
99
+
100
+ expect(cache.map.get('a')).toBe(originalResourceRef);
101
+
102
+ expect(cache.generation).toBe(2);
103
+ expect(cache.revision).toBe(newRevision);
104
+ });
105
+
106
+ it('should remove stale data from list and map (paging)', () => {
107
+ const page1Proxy = new Pod({ id: 'a', name: 'stale-pod' }, ctx);
108
+
109
+ state.types[resourceType] = {
110
+ list: [page1Proxy],
111
+ map: new Map([['a', page1Proxy]]),
112
+ generation: 1,
113
+ };
114
+
115
+ const listRef = state.types[resourceType].list;
116
+
117
+ const page2Data = [
118
+ { id: 'b', name: 'new-pod' },
119
+ ];
120
+
121
+ loadPage(state, {
122
+ type: resourceType, data: page2Data, ctx, pagination, revision
123
+ });
124
+
125
+ const cache = state.types[resourceType];
126
+
127
+ expect(cache.list).toBe(listRef); // List reference must be preserved
128
+
129
+ expect(cache.list).toHaveLength(1);
130
+ expect(cache.list[0].id).toBe('b');
131
+
132
+ expect(cache.map.size).toBe(1);
133
+ expect(cache.map.has('a')).toBe(false); // Stale 'a' is gone
134
+ expect(cache.map.has('b')).toBe(true);
135
+ });
136
+
137
+ it('should handle partial overlaps (update, remove stale, add new)', () => {
138
+ // 1. Pre-load cache
139
+ const staleResource = new Pod({ id: 'a', prop: 'stale-pod' }, ctx);
140
+ const v1Resource = new Pod({ id: 'b', prop: 'v1-pod-b' }, ctx);
141
+
142
+ state.types[resourceType] = {
143
+ list: [staleResource, v1Resource],
144
+ map: new Map([['a', staleResource], ['b', v1Resource]]),
145
+ generation: 1,
146
+ };
147
+
148
+ const listRef = state.types[resourceType].list;
149
+ const v1ResourceRef = v1Resource;
150
+
151
+ const newData = [
152
+ { id: 'b', prop: 'v2-pod-b' }, // Updated
153
+ { id: 'c', prop: 'new-pod-c' }, // New
154
+ ];
155
+
156
+ loadPage(state, {
157
+ type: resourceType, data: newData, ctx, pagination, revision
158
+ });
159
+
160
+ const cache = state.types[resourceType];
161
+
162
+ expect(cache.list).toBe(listRef); // List reference preserved
163
+
164
+ expect(cache.list).toHaveLength(2);
165
+ expect(cache.list[0].id).toBe('b');
166
+ expect(cache.list[1].id).toBe('c');
167
+
168
+ expect(cache.list[0]).toBe(v1ResourceRef);
169
+ expect(v1ResourceRef.prop).toBe('v2-pod-b');
170
+
171
+ expect(cache.list[1]).toStrictEqual(expect.objectContaining({ id: 'c' }));
172
+
173
+ expect(cache.map.size).toBe(2);
174
+ expect(cache.map.has('a')).toBe(false); // Stale, removed
175
+ expect(cache.map.get('b')).toBe(v1ResourceRef); // Updated
176
+ expect(cache.map.get('c')).toBe(cache.list[1]); // Added
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,3 @@
1
+ export default async(context) => {
2
+ await context.store.dispatch('dashboardClientInit', context);
3
+ };