@rancher/shell 0.3.19 → 0.3.21

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 (47) hide show
  1. package/assets/translations/en-us.yaml +4 -1
  2. package/components/PromptModal.vue +4 -0
  3. package/components/Questions/Array.vue +2 -2
  4. package/components/Questions/Boolean.vue +7 -1
  5. package/components/Questions/CloudCredential.vue +1 -0
  6. package/components/Questions/Enum.vue +21 -2
  7. package/components/Questions/Float.vue +8 -3
  8. package/components/Questions/Int.vue +8 -3
  9. package/components/Questions/Question.js +72 -0
  10. package/components/Questions/QuestionMap.vue +2 -1
  11. package/components/Questions/Radio.vue +33 -0
  12. package/components/Questions/Reference.vue +2 -0
  13. package/components/Questions/String.vue +8 -3
  14. package/components/Questions/Yaml.vue +46 -0
  15. package/components/Questions/__tests__/Boolean.test.ts +123 -0
  16. package/components/Questions/__tests__/Float.test.ts +123 -0
  17. package/components/Questions/__tests__/Int.test.ts +123 -0
  18. package/components/Questions/__tests__/String.test.ts +123 -0
  19. package/components/Questions/__tests__/Yaml.test.ts +123 -0
  20. package/components/Questions/index.vue +8 -1
  21. package/components/ResourceTable.vue +10 -13
  22. package/components/SideNav.vue +634 -0
  23. package/components/__tests__/NamespaceFilter.test.ts +3 -4
  24. package/components/form/UnitInput.vue +1 -0
  25. package/components/form/__tests__/KeyValue.test.ts +2 -1
  26. package/components/form/__tests__/UnitInput.test.ts +2 -2
  27. package/components/formatter/LinkName.vue +12 -1
  28. package/components/nav/WorkspaceSwitcher.vue +4 -1
  29. package/core/plugin-helpers.js +4 -1
  30. package/core/types.ts +25 -1
  31. package/detail/node.vue +2 -2
  32. package/edit/fleet.cattle.io.gitrepo.vue +7 -0
  33. package/layouts/default.vue +11 -597
  34. package/middleware/authenticated.js +2 -14
  35. package/models/fleet.cattle.io.gitrepo.js +3 -1
  36. package/package.json +1 -1
  37. package/pages/auth/login.vue +1 -1
  38. package/pages/c/_cluster/fleet/index.vue +4 -0
  39. package/pages/c/_cluster/uiplugins/index.vue +3 -3
  40. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +8 -0
  41. package/rancher-components/components/Form/Radio/RadioButton.test.ts +7 -3
  42. package/store/auth.js +2 -0
  43. package/types/shell/index.d.ts +2 -0
  44. package/utils/auth.js +17 -0
  45. package/utils/object.js +0 -1
  46. package/utils/validators/__tests__/cidr.test.ts +33 -0
  47. package/utils/validators/cidr.js +5 -0
@@ -15,6 +15,7 @@ import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
15
15
  import { AFTER_LOGIN_ROUTE, WORKSPACE } from '@shell/store/prefs';
16
16
  import { BACK_TO } from '@shell/config/local-storage';
17
17
  import { NAME as FLEET_NAME } from '@shell/config/product/fleet.js';
18
+ import { canViewResource } from '@shell/utils/auth';
18
19
 
19
20
  const getPackageFromRoute = (route) => {
20
21
  if (!route?.meta) {
@@ -133,20 +134,7 @@ function invalidResource(store, to, redirect) {
133
134
  return false;
134
135
  }
135
136
 
136
- // Note - don't use the current products store... because products can override stores for resources with `typeStoreMap`
137
- const inStore = store.getters['currentStore'](resource);
138
- // There's a chance we're in an extension's product who's store could be anything, so confirm schemaFor exists
139
- const schemaFor = store.getters[`${ inStore }/schemaFor`];
140
-
141
- // In order to check a resource is valid we need these
142
- if (!inStore || !schemaFor) {
143
- return false;
144
- }
145
-
146
- // Resource is valid if a schema exists for it (standard resource, spoofed resource) or it's a virtual resource
147
- const validResource = schemaFor(resource) || store.getters['type-map/isVirtual'](resource);
148
-
149
- if (validResource) {
137
+ if (canViewResource(store, resource)) {
150
138
  return false;
151
139
  }
152
140
 
@@ -1,3 +1,4 @@
1
+ import Vue from 'vue';
1
2
  import { convert, matching, convertSelectorObj } from '@shell/utils/selector';
2
3
  import jsyaml from 'js-yaml';
3
4
  import { escapeHtml, randomStr } from '@shell/utils/string';
@@ -32,7 +33,8 @@ export default class GitRepo extends SteveModel {
32
33
 
33
34
  spec.paths = spec.paths || [];
34
35
  spec.clientSecretName = spec.clientSecretName || null;
35
- spec.correctDrift = { enabled: false };
36
+
37
+ Vue.set(spec, 'correctDrift', { enabled: false });
36
38
 
37
39
  set(this, 'spec', spec);
38
40
  set(this, 'metadata', meta);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rancher/shell",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "description": "Rancher Dashboard Shell",
5
5
  "repository": "https://github.com/rancherlabs/dashboard",
6
6
  "license": "Apache-2.0",
@@ -149,7 +149,7 @@ export default {
149
149
  return this.t('login.error');
150
150
  }
151
151
 
152
- return this.err;
152
+ return this.err?.length ? this.t('login.specificError', { msg: this.err }) : '';
153
153
  },
154
154
 
155
155
  errorToDisplay() {
@@ -377,6 +377,7 @@ export default {
377
377
  :title="`${t('resourceDetail.masthead.workspace')}: ${ws.nameDisplay}`"
378
378
  :is-collapsed="isCollapsed[ws.id]"
379
379
  :is-title-clickable="true"
380
+ :data-testid="`collapsible-card-${ ws.id }`"
380
381
  @toggleCollapse="toggleCollapse($event, ws.id)"
381
382
  @titleClick="setWorkspaceFilterAndLinkToGitRepo(ws.id)"
382
383
  >
@@ -411,6 +412,7 @@ export default {
411
412
  <span v-if="ws.type === 'namespace'"> - </span>
412
413
  <CompoundStatusBadge
413
414
  v-else
415
+ data-testid="clusters-ready"
414
416
  :tooltip-text="getTooltipInfo('clusters', row)"
415
417
  :badge-class="getStatusInfo('clusters', row).badgeClass"
416
418
  :icon="getStatusInfo('clusters', row).icon"
@@ -421,6 +423,7 @@ export default {
421
423
  <span v-if="ws.type === 'namespace'"> - </span>
422
424
  <CompoundStatusBadge
423
425
  v-else
426
+ data-testid="bundles-ready"
424
427
  :tooltip-text="getTooltipInfo('bundles', row)"
425
428
  :badge-class="getStatusInfo('bundles', row).badgeClass"
426
429
  :icon="getStatusInfo('bundles', row).icon"
@@ -429,6 +432,7 @@ export default {
429
432
  </template>
430
433
  <template #cell:resourcesReady="{row}">
431
434
  <CompoundStatusBadge
435
+ data-testid="resources-ready"
432
436
  :tooltip-text="getTooltipInfo('resources', row)"
433
437
  :badge-class="getStatusInfo('resources', row).badgeClass"
434
438
  :icon="getStatusInfo('resources', row).icon"
@@ -243,9 +243,9 @@ export default {
243
243
 
244
244
  all = all.map((chart) => {
245
245
  // Label can be overridden by chart annotation
246
- const label = uiPluginAnnotation(UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay;
246
+ const label = uiPluginAnnotation(chart, UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME) || chart.chartNameDisplay;
247
247
  const item = {
248
- name: chart.chartNameDisplay,
248
+ name: chart.chartName,
249
249
  label,
250
250
  description: chart.chartDescription,
251
251
  id: chart.id,
@@ -305,7 +305,7 @@ export default {
305
305
  if (!chart) {
306
306
  // A plugin is loaded, but there is no chart, so add an item so that it shows up
307
307
  const rancher = typeof p.metadata?.rancher === 'object' ? p.metadata.rancher : {};
308
- const label = rancher[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || p.name;
308
+ const label = rancher.annotations?.[UI_PLUGIN_CHART_ANNOTATIONS.DISPLAY_NAME] || p.name;
309
309
  const item = {
310
310
  name: p.name,
311
311
  label,
@@ -206,6 +206,13 @@ export default (
206
206
  }
207
207
  },
208
208
 
209
+ /**
210
+ * Emit on input change
211
+ */
212
+ onChange(event: Event): void {
213
+ this.$emit('change', event);
214
+ },
215
+
209
216
  /**
210
217
  * Emit on input with delay. Note: Arrow function is avoided due context
211
218
  * binding.
@@ -299,6 +306,7 @@ export default (
299
306
  @input="onInput($event.target.value)"
300
307
  @focus="onFocus"
301
308
  @blur="onBlur"
309
+ @change="onChange"
302
310
  >
303
311
  </slot>
304
312
 
@@ -4,7 +4,7 @@ import { cleanHtmlDirective } from '@shell/plugins/clean-html-directive';
4
4
 
5
5
  describe('radioButton.vue', () => {
6
6
  it('renders label slot contents', () => {
7
- const wrapper = shallowMount(RadioButton, { slots: { label: 'Test Label' } });
7
+ const wrapper = shallowMount(RadioButton, { slots: { label: 'Test Label' }, propsData: { val: {}, value: {} } });
8
8
 
9
9
  expect(wrapper.find('.radio-label').text()).toBe('Test Label');
10
10
  });
@@ -14,7 +14,9 @@ describe('radioButton.vue', () => {
14
14
  RadioButton,
15
15
  {
16
16
  directives: { cleanHtmlDirective },
17
- propsData: { label: 'Test Label' }
17
+ propsData: {
18
+ label: 'Test Label', val: {}, value: {}
19
+ }
18
20
  });
19
21
 
20
22
  expect(wrapper.find('.radio-label').text()).toBe('Test Label');
@@ -23,7 +25,9 @@ describe('radioButton.vue', () => {
23
25
  it('renders slot contents when both slot and label prop are provided', () => {
24
26
  const wrapper = shallowMount(RadioButton, {
25
27
  slots: { label: 'Test Label - Slot' },
26
- propsData: { label: 'Test Label - Props' },
28
+ propsData: {
29
+ label: 'Test Label - Props', val: {}, value: {}
30
+ },
27
31
  });
28
32
 
29
33
  expect(wrapper.find('.radio-label').text()).toBe('Test Label - Slot');
package/store/auth.js CHANGED
@@ -334,6 +334,8 @@ export const actions = {
334
334
  } catch (err) {
335
335
  if (err._status === 401) {
336
336
  return Promise.reject(LOGIN_ERRORS.CLIENT_UNAUTHORIZED);
337
+ } else if (err.message) {
338
+ return Promise.reject(err.message);
337
339
  } else if ( err._status >= 400 && err._status <= 499 ) {
338
340
  return Promise.reject(LOGIN_ERRORS.CLIENT);
339
341
  }
@@ -3029,6 +3029,7 @@ export function authProvidersInfo(store: any): Promise<{
3029
3029
  }>;
3030
3030
  export function checkSchemasForFindAllHash(types: any, store: any): any;
3031
3031
  export function checkPermissions(types: any, getters: any): any;
3032
+ export function canViewResource(store: any, resource: any): boolean;
3032
3033
  }
3033
3034
 
3034
3035
  // @shell/utils/aws
@@ -4060,6 +4061,7 @@ export default _default;
4060
4061
 
4061
4062
  declare module '@shell/utils/validators/cidr' {
4062
4063
  export function isValidCIDR(cidr: any): boolean;
4064
+ export function isValidIP(ip: any): boolean;
4063
4065
  export function isValidMac(value: any): boolean;
4064
4066
  }
4065
4067
 
package/utils/auth.js CHANGED
@@ -144,3 +144,20 @@ export const checkPermissions = (types, getters) => {
144
144
 
145
145
  return allHash(hash);
146
146
  };
147
+
148
+ export const canViewResource = (store, resource) => {
149
+ // Note - don't use the current products store... because products can override stores for resources with `typeStoreMap`
150
+ const inStore = store.getters['currentStore'](resource);
151
+ // There's a chance we're in an extension's product who's store could be anything, so confirm schemaFor exists
152
+ const schemaFor = store.getters[`${ inStore }/schemaFor`];
153
+
154
+ // In order to check a resource is valid we need these
155
+ if (!inStore || !schemaFor) {
156
+ return false;
157
+ }
158
+
159
+ // Resource is valid if a schema exists for it (standard resource, spoofed resource) or it's a virtual resource
160
+ const validResource = schemaFor(resource) || store.getters['type-map/isVirtual'](resource);
161
+
162
+ return !!validResource;
163
+ };
package/utils/object.js CHANGED
@@ -72,7 +72,6 @@ export function get(obj, path) {
72
72
  return '(JSON Path err)';
73
73
  }
74
74
  }
75
-
76
75
  if ( !path.includes('.') ) {
77
76
  return obj?.[path];
78
77
  }
@@ -0,0 +1,33 @@
1
+ import { isValidCIDR, isValidIP, isValidMac } from '@shell/utils/validators/cidr';
2
+
3
+ describe('fx: isValidCIDR', () => {
4
+ it('should be valid', () => {
5
+ expect(isValidCIDR('10.42.0.0/8')).toBe(true);
6
+ });
7
+ it('should be invalid', () => {
8
+ expect(isValidCIDR('10.42.0.0')).toBe(false);
9
+ expect(isValidCIDR('10.42.0.0/500')).toBe(false);
10
+ expect(isValidCIDR('300.42.0.0/8')).toBe(false);
11
+ });
12
+ });
13
+
14
+ describe('fx: isValidIP', () => {
15
+ it('should be valid', () => {
16
+ expect(isValidIP('10.42.0.1')).toBe(true);
17
+ });
18
+ it('should be invalid', () => {
19
+ expect(isValidIP('10.42.0.0/8')).toBe(false);
20
+ expect(isValidIP('300.42.0.0')).toBe(false);
21
+ });
22
+ });
23
+
24
+ describe('fx: isValidMac', () => {
25
+ it('should be valid', () => {
26
+ expect(isValidMac('00-08-20-83-53-D1')).toBe(true);
27
+ expect(isValidMac('00-08-20-83-53-d1')).toBe(true);
28
+ });
29
+ it('should be invalid', () => {
30
+ expect(isValidMac('invalid')).toBe(false);
31
+ expect(isValidMac('00-08-20-83-53')).toBe(false);
32
+ });
33
+ });
@@ -1,9 +1,14 @@
1
1
  const validCIDRregex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|2[0-9]|1[0-9]|[0-9])$/;
2
+ const validIPRegex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
2
3
 
3
4
  export function isValidCIDR(cidr) {
4
5
  return !!cidr.match(validCIDRregex);
5
6
  }
6
7
 
8
+ export function isValidIP(ip) {
9
+ return !!ip.match(validIPRegex);
10
+ }
11
+
7
12
  export function isValidMac(value) {
8
13
  return /^[A-Fa-f0-9]{2}(-[A-Fa-f0-9]{2}){5}$|^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){5}$/.test(value);
9
14
  }