@rancher/shell 3.0.6 → 3.0.7

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 (78) hide show
  1. package/assets/images/pl/dark/rancher-logo.svg +131 -44
  2. package/assets/images/pl/rancher-logo.svg +120 -44
  3. package/assets/styles/base/_basic.scss +2 -2
  4. package/assets/styles/base/_color-classic.scss +51 -0
  5. package/assets/styles/base/_color.scss +3 -3
  6. package/assets/styles/base/_mixins.scss +1 -1
  7. package/assets/styles/base/_variables-classic.scss +47 -0
  8. package/assets/styles/global/_button.scss +49 -17
  9. package/assets/styles/global/_form.scss +1 -1
  10. package/assets/styles/themes/_dark.scss +4 -0
  11. package/assets/styles/themes/_light.scss +3 -69
  12. package/assets/styles/themes/_modern.scss +194 -50
  13. package/assets/styles/vendor/vue-select.scss +1 -2
  14. package/assets/translations/en-us.yaml +33 -21
  15. package/components/ClusterIconMenu.vue +1 -1
  16. package/components/ClusterProviderIcon.vue +1 -1
  17. package/components/CodeMirror.vue +1 -1
  18. package/components/IconOrSvg.vue +40 -29
  19. package/components/ResourceDetail/index.vue +1 -0
  20. package/components/SortableTable/sorting.js +3 -1
  21. package/components/Tabbed/index.vue +5 -5
  22. package/components/form/ResourceTabs/index.vue +37 -18
  23. package/components/form/SecretSelector.vue +6 -2
  24. package/components/nav/Group.vue +29 -9
  25. package/components/nav/Header.vue +6 -8
  26. package/components/nav/NamespaceFilter.vue +1 -1
  27. package/components/nav/TopLevelMenu.helper.ts +47 -20
  28. package/components/nav/TopLevelMenu.vue +44 -14
  29. package/components/nav/Type.vue +0 -5
  30. package/components/nav/__tests__/TopLevelMenu.test.ts +2 -0
  31. package/config/pagination-table-headers.js +10 -2
  32. package/config/product/explorer.js +4 -3
  33. package/config/table-headers.js +9 -0
  34. package/core/plugin.ts +18 -6
  35. package/core/types.ts +8 -0
  36. package/detail/provisioning.cattle.io.cluster.vue +1 -0
  37. package/dialog/InstallExtensionDialog.vue +71 -45
  38. package/dialog/UninstallExtensionDialog.vue +2 -1
  39. package/dialog/__tests__/InstallExtensionDialog.test.ts +111 -0
  40. package/edit/auth/oidc.vue +86 -16
  41. package/mixins/__tests__/chart.test.ts +1 -1
  42. package/mixins/chart.js +1 -1
  43. package/models/event.js +7 -0
  44. package/models/provisioning.cattle.io.cluster.js +9 -0
  45. package/package.json +1 -1
  46. package/pages/c/_cluster/explorer/EventsTable.vue +3 -6
  47. package/pages/c/_cluster/settings/performance.vue +1 -1
  48. package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +159 -62
  49. package/pages/c/_cluster/uiplugins/__tests__/PluginInfoPanel.test.ts +102 -0
  50. package/pages/c/_cluster/uiplugins/__tests__/{index.spec.ts → index.test.ts} +121 -55
  51. package/pages/c/_cluster/uiplugins/index.vue +110 -94
  52. package/plugins/__tests__/subscribe.events.test.ts +194 -0
  53. package/plugins/dashboard-store/actions.js +3 -0
  54. package/plugins/dashboard-store/getters.js +1 -1
  55. package/plugins/dashboard-store/resource-class.js +3 -3
  56. package/plugins/steve/__tests__/subscribe.spec.ts +27 -24
  57. package/plugins/steve/index.js +18 -10
  58. package/plugins/steve/mutations.js +2 -2
  59. package/plugins/steve/resourceWatcher.js +2 -2
  60. package/plugins/steve/steve-pagination-utils.ts +12 -9
  61. package/plugins/steve/subscribe.js +113 -85
  62. package/plugins/subscribe-events.ts +211 -0
  63. package/rancher-components/BadgeState/BadgeState.vue +8 -6
  64. package/rancher-components/Banner/Banner.vue +2 -1
  65. package/rancher-components/Form/Checkbox/Checkbox.vue +3 -3
  66. package/rancher-components/Form/Radio/RadioButton.vue +3 -3
  67. package/store/index.js +12 -22
  68. package/types/extension-manager.ts +8 -1
  69. package/types/resources/settings.d.ts +24 -17
  70. package/types/shell/index.d.ts +352 -335
  71. package/types/store/subscribe-events.types.ts +70 -0
  72. package/types/store/subscribe.types.ts +6 -22
  73. package/utils/pagination-utils.ts +87 -28
  74. package/utils/pagination-wrapper.ts +6 -8
  75. package/utils/sort.js +5 -0
  76. package/utils/unit-tests/pagination-utils.spec.ts +283 -0
  77. package/utils/validators/formRules/__tests__/index.test.ts +7 -0
  78. package/utils/validators/formRules/index.ts +2 -2
@@ -0,0 +1,111 @@
1
+ import { shallowMount, VueWrapper } from '@vue/test-utils';
2
+ import InstallExtensionDialog from '@shell/dialog/InstallExtensionDialog.vue';
3
+
4
+ jest.mock('@shell/config/uiplugins', () => ({
5
+ ...jest.requireActual('@shell/config/uiplugins'),
6
+ isChartVersionHigher: jest.fn((v1: string, v2: string) => v1 > v2),
7
+ }));
8
+
9
+ const t = (key: string): string => key;
10
+
11
+ describe('component: InstallExtensionDialog', () => {
12
+ let wrapper: VueWrapper<any>;
13
+
14
+ const mountComponent = (propsData = {}) => {
15
+ const store = { dispatch: () => Promise.resolve() };
16
+
17
+ const defaultProps = {
18
+ plugin: { installableVersions: [] },
19
+ action: 'install',
20
+ updateStatus: jest.fn(),
21
+ closed: jest.fn(),
22
+ };
23
+
24
+ return shallowMount(InstallExtensionDialog, {
25
+ propsData: {
26
+ ...defaultProps,
27
+ ...propsData,
28
+ },
29
+ global: {
30
+ mocks: {
31
+ $store: store,
32
+ t,
33
+ },
34
+ }
35
+ });
36
+ };
37
+
38
+ describe('fetch', () => {
39
+ it('should set currentVersion if plugin is installed', async() => {
40
+ wrapper = mountComponent({ plugin: { installed: true, installedVersion: '1.0.0' } });
41
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
42
+ expect(wrapper.vm.currentVersion).toBe('1.0.0');
43
+ });
44
+
45
+ it('should set version from initialVersion if provided', async() => {
46
+ wrapper = mountComponent({ initialVersion: '1.2.3', plugin: { installed: false } });
47
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
48
+ expect(wrapper.vm.version).toBe('1.2.3');
49
+ });
50
+
51
+ it('should set version to latest for upgrade action', async() => {
52
+ const plugin = { installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }] };
53
+
54
+ wrapper = mountComponent({ plugin, action: 'upgrade' });
55
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
56
+ expect(wrapper.vm.version).toBe('1.1.0');
57
+ });
58
+
59
+ it('should set version to next oldest for downgrade action', async() => {
60
+ const plugin = {
61
+ installed: true,
62
+ installedVersion: '1.1.0',
63
+ installableVersions: [{ version: '1.1.0' }, { version: '1.0.0' }]
64
+ };
65
+
66
+ wrapper = mountComponent({ plugin, action: 'downgrade' });
67
+ await wrapper.vm.$options.fetch.call(wrapper.vm);
68
+ expect(wrapper.vm.version).toBe('1.0.0');
69
+ });
70
+ });
71
+
72
+ describe('versionOptions', () => {
73
+ it('should include and disable the current version', () => {
74
+ const plugin = {
75
+ installableVersions: [
76
+ { version: '1.1.0' },
77
+ { version: '1.0.0' },
78
+ ]
79
+ };
80
+
81
+ wrapper = mountComponent({ plugin });
82
+ wrapper.vm.currentVersion = '1.0.0';
83
+
84
+ const options = wrapper.vm.versionOptions;
85
+ const currentOption = options.find((o: any) => o.value === '1.0.0');
86
+
87
+ expect(currentOption).toBeDefined();
88
+ expect(currentOption.disabled).toBe(true);
89
+ expect(currentOption.label).toContain('(plugins.labels.current)');
90
+ });
91
+ });
92
+
93
+ describe('buttonMode', () => {
94
+ beforeEach(() => {
95
+ wrapper = mountComponent({ action: 'upgrade' });
96
+ wrapper.vm.currentVersion = '1.0.0';
97
+ });
98
+
99
+ it('should be "upgrade" if selected version is higher', async() => {
100
+ wrapper.vm.version = '1.1.0';
101
+ await wrapper.vm.$nextTick();
102
+ expect(wrapper.vm.buttonMode).toBe('upgrade');
103
+ });
104
+
105
+ it('should be "downgrade" if selected version is lower', async() => {
106
+ wrapper.vm.version = '0.9.0';
107
+ await wrapper.vm.$nextTick();
108
+ expect(wrapper.vm.buttonMode).toBe('downgrade');
109
+ });
110
+ });
111
+ });
@@ -59,11 +59,16 @@ export default {
59
59
  userInfoEndpoint: null,
60
60
  },
61
61
  // TODO #13457: this is duplicated due wrong format
62
- oidcScope: [],
63
- SLO_OPTION_VALUES
62
+ oidcScope: [],
63
+ SLO_OPTION_VALUES,
64
+ addCustomClaims: false,
64
65
  };
65
66
  },
66
67
 
68
+ created() {
69
+ this.registerBeforeHook(this.willSave, 'willSave');
70
+ },
71
+
67
72
  computed: {
68
73
  tArgs() {
69
74
  return {
@@ -140,6 +145,10 @@ export default {
140
145
  return this.model?.id === 'cognito';
141
146
  },
142
147
 
148
+ isGenericOidc() {
149
+ return this.model?.id === 'genericoidc';
150
+ },
151
+
143
152
  isLogoutAllSupported() {
144
153
  return this.model?.logoutAllSupported;
145
154
  },
@@ -220,6 +229,15 @@ export default {
220
229
  this.model.logoutAllForced = false;
221
230
  break;
222
231
  }
232
+ },
233
+
234
+ model: {
235
+ handler(newVal) {
236
+ if (newVal?.nameClaim || newVal?.groupsClaim || newVal?.emailClaim) {
237
+ this.addCustomClaims = true;
238
+ }
239
+ },
240
+ once: true
223
241
  }
224
242
  },
225
243
 
@@ -248,6 +266,14 @@ export default {
248
266
 
249
267
  updateScope() {
250
268
  this.model.scope = this.oidcScope.join(' ');
269
+ },
270
+
271
+ willSave() {
272
+ if (this.isGenericOidc && !this.addCustomClaims) {
273
+ this.model.nameClaim = undefined;
274
+ this.model.groupsClaim = undefined;
275
+ this.model.emailClaim = undefined;
276
+ }
251
277
  }
252
278
  }
253
279
  };
@@ -390,21 +416,60 @@ export default {
390
416
  </div>
391
417
  </div>
392
418
 
393
- <!-- Allow group search -->
394
- <div
395
- v-if="supportsGroupSearch"
396
- class="row mb-20"
397
- >
398
- <div class="col span-6">
399
- <Checkbox
400
- v-model:value="model.groupSearchEnabled"
401
- data-testid="input-group-search"
402
- :label="t('authConfig.oidc.groupSearch.label')"
403
- :tooltip="t('authConfig.oidc.groupSearch.tooltip')"
404
- :mode="mode"
405
- />
419
+ <template v-if="isGenericOidc || supportsGroupSearch">
420
+ <div
421
+ class="row mb-20"
422
+ >
423
+ <div class="col span-6 checkbox-flex">
424
+ <!-- Allow group search -->
425
+ <Checkbox
426
+ v-if="supportsGroupSearch"
427
+ v-model:value="model.groupSearchEnabled"
428
+ data-testid="input-group-search"
429
+ :label="t('authConfig.oidc.groupSearch.label')"
430
+ :tooltip="t('authConfig.oidc.groupSearch.tooltip')"
431
+ :mode="mode"
432
+ />
433
+ <Checkbox
434
+ v-if="isGenericOidc"
435
+ v-model:value="addCustomClaims"
436
+ data-testid="input-add-custom-claims"
437
+ :label="t('authConfig.oidc.customClaims.enable.label')"
438
+ :tooltip="t('authConfig.oidc.customClaims.enable.tooltip')"
439
+ :mode="mode"
440
+ />
441
+ </div>
406
442
  </div>
407
- </div>
443
+ </template>
444
+
445
+ <template v-if="addCustomClaims">
446
+ <h4>{{ t('authConfig.oidc.customClaims.label') }}</h4>
447
+ <div class="row mb-20">
448
+ <div class="col span-6">
449
+ <LabeledInput
450
+ v-model:value="model.nameClaim"
451
+ :label="t(`authConfig.oidc.customClaims.nameClaim.label`)"
452
+ :mode="mode"
453
+ />
454
+ </div>
455
+ <div class="col span-6">
456
+ <LabeledInput
457
+ v-model:value="model.groupsClaim"
458
+ :label="t(`authConfig.oidc.customClaims.groupsClaim.label`)"
459
+ :mode="mode"
460
+ />
461
+ </div>
462
+ </div>
463
+ <div class="row mb-20">
464
+ <div class="col span-6">
465
+ <LabeledInput
466
+ v-model:value="model.emailClaim"
467
+ :label="t(`authConfig.oidc.customClaims.emailClaim.label`)"
468
+ :mode="mode"
469
+ />
470
+ </div>
471
+ </div>
472
+ </template>
408
473
 
409
474
  <!-- Scopes -->
410
475
  <div class="row mb-20">
@@ -608,4 +673,9 @@ export default {
608
673
  margin: 0 3px;
609
674
  }
610
675
  }
676
+
677
+ .checkbox-flex {
678
+ display: flex;
679
+ flex-direction: column;
680
+ }
611
681
  </style>
@@ -213,7 +213,7 @@ describe('chartMixin', () => {
213
213
  });
214
214
 
215
215
  expect(wrapper.vm.action).toStrictEqual({
216
- name: 'upgradeVersion',
216
+ name: 'upgrade',
217
217
  tKey: 'upgrade',
218
218
  icon: 'icon-upgrade-alt',
219
219
  });
package/mixins/chart.js CHANGED
@@ -242,7 +242,7 @@ export default {
242
242
 
243
243
  if (compare(this.currentVersion, this.targetVersion) < 0) {
244
244
  return {
245
- name: 'upgradeVersion', tKey: 'upgrade', icon: 'icon-upgrade-alt'
245
+ name: 'upgrade', tKey: 'upgrade', icon: 'icon-upgrade-alt'
246
246
  };
247
247
  }
248
248
 
package/models/event.js CHANGED
@@ -25,6 +25,13 @@ export default class K8sEvent extends SteveModel {
25
25
  return this._type;
26
26
  }
27
27
 
28
+ get firstSeen() {
29
+ const schema = this.$getters['schemaFor'](this.type);
30
+ const rowValueGetter = this.$rootGetters['type-map/rowValueGetter'];
31
+
32
+ return schema && rowValueGetter ? rowValueGetter(schema, 'First Seen')(this) : null;
33
+ }
34
+
28
35
  get lastSeen() {
29
36
  const schema = this.$getters['schemaFor'](this.type);
30
37
  const rowValueGetter = this.$rootGetters['type-map/rowValueGetter'];
@@ -391,6 +391,15 @@ export default class ProvCluster extends SteveModel {
391
391
  const pCluster = this.$rootGetters['management/byId'](CAPI.RANCHER_CLUSTER, this.id);
392
392
  const name = this.status?.clusterName || pCluster?.status?.clusterName;
393
393
 
394
+ try {
395
+ if (name) {
396
+ // Just in case we're not generically watching all mgmt clusters and...
397
+ // thus won't receive new mgmt cluster over socket...
398
+ // fire and forget a request to fetch it (this won't make multiple requests if one is already running)
399
+ this.$dispatch('find', { type: MANAGEMENT.CLUSTER, id: name });
400
+ }
401
+ } catch {}
402
+
394
403
  return name && !!this.$rootGetters['management/byId'](MANAGEMENT.CLUSTER, name);
395
404
  }, this.$rootGetters['i18n/t']('cluster.managementTimeout'), timeout, interval);
396
405
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "3.0.6",
3
+ "version": "3.0.7",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -3,7 +3,7 @@
3
3
  import { MESSAGE, NAME, OBJECT, REASON } from '@shell/config/table-headers';
4
4
  import { EVENT } from '@shell/config/types';
5
5
  import PaginatedResourceTable from '@shell/components/PaginatedResourceTable';
6
- import { STEVE_EVENT_OBJECT, STEVE_NAME_COL } from '@shell/config/pagination-table-headers';
6
+ import { STEVE_EVENT_FIRST_SEEN, STEVE_EVENT_LAST_SEEN, STEVE_EVENT_OBJECT, STEVE_NAME_COL } from '@shell/config/pagination-table-headers';
7
7
  import { headerFromSchemaColString } from '@shell/store/type-map.utils';
8
8
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
9
9
 
@@ -59,11 +59,8 @@ export default {
59
59
  ...STEVE_NAME_COL,
60
60
  defaultSort: false,
61
61
  },
62
- headerFromSchemaColString('First Seen', schema, this.$store.getters, true),
63
- {
64
- ...headerFromSchemaColString('Last Seen', schema, this.$store.getters, true),
65
- defaultSort: true,
66
- },
62
+ STEVE_EVENT_FIRST_SEEN,
63
+ STEVE_EVENT_LAST_SEEN,
67
64
  headerFromSchemaColString('Count', schema, this.$store.getters, true),
68
65
  ] : [];
69
66
 
@@ -105,7 +105,7 @@ export default {
105
105
  if (settings.resources.enableAll) {
106
106
  resources.push(this.t('performance.serverPagination.resources.all'));
107
107
  } else {
108
- settings.resources.enableSome.enabled.forEach((resource) => {
108
+ settings.resources.enableSome.enabled?.forEach((resource) => {
109
109
  resources.push(!!resource.length ? resource : `${ resource.resource } (${ resource.context })`);
110
110
  });
111
111
  if (settings.resources.enableSome.generic) {