@rancher/shell 0.5.1 → 0.5.3

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 (70) hide show
  1. package/assets/translations/en-us.yaml +8 -4
  2. package/components/ClusterIconMenu.vue +24 -9
  3. package/components/CodeMirror.vue +79 -18
  4. package/components/FixedBanner.vue +1 -0
  5. package/components/ResourceDetail/index.vue +1 -4
  6. package/components/ResourceYaml.vue +29 -5
  7. package/components/SideNav.vue +42 -64
  8. package/components/SortableTable/index.vue +1 -1
  9. package/components/YamlEditor.vue +1 -0
  10. package/components/__tests__/CodeMirror.spec.ts +99 -0
  11. package/components/form/BannerSettings.vue +3 -0
  12. package/components/form/FileSelector.vue +1 -0
  13. package/components/form/KeyValue.vue +1 -0
  14. package/components/formatter/WorkloadDetailEndpoints.vue +12 -22
  15. package/components/formatter/__tests__/WorkloadDetailEndpoints.test.ts +81 -0
  16. package/components/nav/Header.vue +1 -0
  17. package/components/nav/Jump.vue +19 -9
  18. package/components/nav/TopLevelMenu.vue +37 -15
  19. package/components/nav/Type.vue +15 -4
  20. package/components/nav/__tests__/TopLevelMenu.test.ts +1 -1
  21. package/components/nav/__tests__/Type.test.ts +30 -0
  22. package/core/types-provisioning.ts +7 -0
  23. package/detail/__tests__/provisioning.cattle.io.cluster.test.ts +77 -0
  24. package/detail/fleet.cattle.io.bundle.vue +1 -1
  25. package/detail/provisioning.cattle.io.cluster.vue +19 -4
  26. package/edit/management.cattle.io.setting.vue +1 -0
  27. package/edit/monitoring.coreos.com.alertmanagerconfig/types/opsgenie.vue +1 -1
  28. package/edit/monitoring.coreos.com.alertmanagerconfig/types/pagerduty.vue +1 -2
  29. package/edit/monitoring.coreos.com.alertmanagerconfig/types/slack.vue +1 -1
  30. package/edit/provisioning.cattle.io.cluster/index.vue +23 -10
  31. package/edit/provisioning.cattle.io.cluster/rke2.vue +22 -50
  32. package/edit/provisioning.cattle.io.cluster/tabs/Basics.vue +9 -11
  33. package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +3 -1
  34. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +3 -0
  35. package/edit/token.vue +1 -0
  36. package/list/catalog.cattle.io.app.vue +1 -0
  37. package/list/management.cattle.io.setting.vue +1 -0
  38. package/machine-config/amazonec2.vue +1 -0
  39. package/models/__tests__/provisioning.cattle.io.cluster.test.ts +151 -0
  40. package/models/__tests__/secret.test.ts +37 -0
  41. package/models/__tests__/storage.k8s.io.storageclass.test.ts +22 -0
  42. package/models/management.cattle.io.kontainerdriver.js +2 -1
  43. package/models/provisioning.cattle.io.cluster.js +36 -1
  44. package/models/secret.js +9 -0
  45. package/models/storage.k8s.io.storageclass.js +1 -1
  46. package/package.json +1 -1
  47. package/pages/c/_cluster/settings/DefaultLinksEditor.vue +1 -0
  48. package/pages/c/_cluster/settings/brand.vue +3 -0
  49. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +4 -4
  50. package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +5 -2
  51. package/pages/c/_cluster/uiplugins/__tests__/AddExtensionRepos.test.ts +96 -0
  52. package/pages/c/_cluster/uiplugins/__tests__/SetupUIPlugins.test.ts +128 -0
  53. package/plugins/dashboard-store/__tests__/actions.test.ts +196 -111
  54. package/plugins/dashboard-store/actions.js +4 -6
  55. package/plugins/dashboard-store/getters.js +60 -2
  56. package/plugins/dashboard-store/resource-class.js +6 -2
  57. package/plugins/steve/__tests__/getters.spec.ts +10 -0
  58. package/plugins/steve/__tests__/resource-utils.test.ts +159 -0
  59. package/plugins/steve/actions.js +3 -37
  60. package/plugins/steve/getters.js +6 -0
  61. package/plugins/steve/resource-utils.ts +38 -0
  62. package/scripts/extension/parse-tag-name +3 -3
  63. package/store/__tests__/type-map.test.ts +1122 -0
  64. package/store/index.js +3 -2
  65. package/store/plugins.js +7 -6
  66. package/store/type-map.js +145 -75
  67. package/types/shell/index.d.ts +2 -0
  68. package/utils/__tests__/create-yaml.test.ts +10 -0
  69. package/utils/create-yaml.js +5 -1
  70. package/utils/object.js +10 -0
@@ -1,127 +1,127 @@
1
1
  import _actions from '@shell/plugins/dashboard-store/actions';
2
2
 
3
- const { findAll } = _actions;
3
+ const { findAll, findMatching } = _actions;
4
4
 
5
5
  describe('dashboard-store: actions', () => {
6
- const setupContext = () => {
7
- const commit = jest.fn();
8
- const dispatch = jest.fn((...args) => {
9
- switch (args[0]) {
10
- case 'request':
11
- return { data: ['requestData'] };
12
- }
13
- });
14
- const state = { config: { namespace: 'unitTest' } };
15
- const getters = {
16
- normalizeType: jest.fn(() => 'getters.normalizeType'),
17
- typeRegistered: jest.fn(() => false),
18
- haveAll: jest.fn(() => false),
19
- haveAllNamespace: jest.fn(() => false),
20
- all: jest.fn(() => 'getters.all'),
21
- urlFor: jest.fn(() => 'getters.urlFor'), // we're not testing the urlFor getter so we don't need to do anything with opt here
22
- };
23
- const rootGetters = {
24
- 'type-map/optionsFor': jest.fn(),
25
- 'auth/fromHeader': 'foo'
26
- };
6
+ describe('findAll', () => {
7
+ // Note - there are TS errors alll over this describe and should not have merged with them in.
8
+ const setupContext = () => {
9
+ const commit = jest.fn();
10
+ const dispatch = jest.fn((...args) => {
11
+ switch (args[0]) {
12
+ case 'request':
13
+ return { data: ['requestData'] };
14
+ }
15
+ });
16
+ const state = { config: { namespace: 'unitTest' } };
17
+ const getters = {
18
+ normalizeType: jest.fn(() => 'getters.normalizeType'),
19
+ typeRegistered: jest.fn(() => false),
20
+ haveAll: jest.fn(() => false),
21
+ haveAllNamespace: jest.fn(() => false),
22
+ all: jest.fn(() => 'getters.all'),
23
+ urlFor: jest.fn(() => 'getters.urlFor'), // we're not testing the urlFor getter so we don't need to do anything with opt here
24
+ };
25
+ const rootGetters = {
26
+ 'type-map/optionsFor': jest.fn(),
27
+ 'auth/fromHeader': 'foo'
28
+ };
27
29
 
28
- // we're not testing function output based off of state or getter inputs here since they are dependencies and should be tested independently
29
- return {
30
- state,
31
- getters,
32
- rootGetters,
33
- commit,
34
- dispatch
30
+ // we're not testing function output based off of state or getter inputs here since they are dependencies and should be tested independently
31
+ return {
32
+ state,
33
+ getters,
34
+ rootGetters,
35
+ commit,
36
+ dispatch
37
+ };
35
38
  };
36
- };
37
39
 
38
- const standardAssertions = {
39
- returnsPromise:
40
- {
40
+ const standardAssertions = {
41
+ returnsPromise: {
41
42
  assertionLabel: 'returns a promise',
42
43
  valueGetter: ({ findAllPromise }) => typeof findAllPromise.then,
43
44
  valueExpected: 'function'
44
45
  },
45
- callsAll: {
46
- assertionLabel: 'calls the "all" getter with the normalizedType',
47
- valueGetter: ({ getters }) => getters.all.mock.calls[0][0],
48
- valueExpected: 'getters.normalizeType'
49
- },
50
- returnsFromAll: {
51
- assertionLabel: 'returns the value expected from the "all" getter',
52
- valueGetter: ({ findAllReturnValue }) => findAllReturnValue,
53
- valueExpected: 'getters.all'
54
- },
55
- firstDispatchAction: {
56
- assertionLabel: 'first dispatch should be the "request" action',
57
- valueGetter: ({ dispatch }) => dispatch.mock.calls[0][0],
58
- valueExpected: 'request'
59
- },
60
- firstDispatchParams: {
61
- assertionLabel: 'first dispatch parameters should be provided a normalized type and a url, streaming, and "metadata.managedFields" excluded under opt',
62
- valueGetter: ({ dispatch }) => dispatch.mock.calls[0][1],
63
- valueExpected: {
64
- type: 'getters.normalizeType',
65
- opt: {
66
- url: 'getters.urlFor',
67
- stream: true
68
- }
46
+ callsAll: {
47
+ assertionLabel: 'calls the "all" getter with the normalizedType',
48
+ valueGetter: ({ getters }) => getters.all.mock.calls[0][0],
49
+ valueExpected: 'getters.normalizeType'
50
+ },
51
+ returnsFromAll: {
52
+ assertionLabel: 'returns the value expected from the "all" getter',
53
+ valueGetter: ({ findAllReturnValue }) => findAllReturnValue,
54
+ valueExpected: 'getters.all'
69
55
  },
70
- assertionMethod: 'toMatchObject'
71
- },
72
- secondDispatchAction: {
73
- assertionLabel: 'second dispatch should be the "watch" action',
74
- valueGetter: ({ dispatch }) => dispatch.mock.calls[1][0],
75
- valueExpected: 'watch'
76
- },
77
- secondDispatchParams: {
78
- assertionLabel: 'second dispatch parameters should have a normalized type and force set to false',
79
- valueGetter: ({ dispatch }) => dispatch.mock.calls[1][1],
80
- valueExpected: { type: 'getters.normalizeType', force: false },
81
- assertionMethod: 'toMatchObject'
82
- },
83
- countDispatches: {
84
- assertionLabel: 'should only make two dispatches',
85
- valueGetter: ({ dispatch }) => dispatch.mock.calls,
86
- valueExpected: 2,
87
- assertionMethod: 'toHaveLength'
88
- },
89
- firstCommitMutation: {
90
- assertionLabel: 'first commit should be the "registerType" mutation',
91
- valueGetter: ({ commit }) => commit.mock.calls[0][0],
92
- valueExpected: 'registerType'
93
- },
94
- firstCommitParams: {
95
- assertionLabel: 'first commit parameter should be a normalized type',
96
- valueGetter: ({ commit }) => commit.mock.calls[0][1],
97
- valueExpected: 'getters.normalizeType'
98
- },
99
- secondCommitMutation: {
100
- assertionLabel: 'second commit should be the "loadAll" mutation',
101
- valueGetter: ({ commit }) => commit.mock.calls[1][0],
102
- valueExpected: 'loadAll'
103
- },
104
- secondCommitParams: {
105
- assertionLabel: 'second commit parameters should have a normalized type, ctx.state.config.namespace, data returned by request, and skipHaveAll set to false',
106
- valueGetter: ({ commit }) => commit.mock.calls[1][1],
107
- valueExpected: {
108
- type: 'getters.normalizeType',
109
- ctx: { state: { config: { namespace: 'unitTest' } } },
110
- data: ['requestData'],
111
- skipHaveAll: false,
56
+ firstDispatchAction: {
57
+ assertionLabel: 'first dispatch should be the "request" action',
58
+ valueGetter: ({ dispatch }) => dispatch.mock.calls[0][0],
59
+ valueExpected: 'request'
60
+ },
61
+ firstDispatchParams: {
62
+ assertionLabel: 'first dispatch parameters should be provided a normalized type and a url, streaming, and "metadata.managedFields" excluded under opt',
63
+ valueGetter: ({ dispatch }) => dispatch.mock.calls[0][1],
64
+ valueExpected: {
65
+ type: 'getters.normalizeType',
66
+ opt: {
67
+ url: 'getters.urlFor',
68
+ stream: true
69
+ }
70
+ },
71
+ assertionMethod: 'toMatchObject'
72
+ },
73
+ secondDispatchAction: {
74
+ assertionLabel: 'second dispatch should be the "watch" action',
75
+ valueGetter: ({ dispatch }) => dispatch.mock.calls[1][0],
76
+ valueExpected: 'watch'
77
+ },
78
+ secondDispatchParams: {
79
+ assertionLabel: 'second dispatch parameters should have a normalized type and force set to false',
80
+ valueGetter: ({ dispatch }) => dispatch.mock.calls[1][1],
81
+ valueExpected: { type: 'getters.normalizeType', force: false },
82
+ assertionMethod: 'toMatchObject'
83
+ },
84
+ countDispatches: {
85
+ assertionLabel: 'should only make two dispatches',
86
+ valueGetter: ({ dispatch }) => dispatch.mock.calls,
87
+ valueExpected: 2,
88
+ assertionMethod: 'toHaveLength'
89
+ },
90
+ firstCommitMutation: {
91
+ assertionLabel: 'first commit should be the "registerType" mutation',
92
+ valueGetter: ({ commit }) => commit.mock.calls[0][0],
93
+ valueExpected: 'registerType'
94
+ },
95
+ firstCommitParams: {
96
+ assertionLabel: 'first commit parameter should be a normalized type',
97
+ valueGetter: ({ commit }) => commit.mock.calls[0][1],
98
+ valueExpected: 'getters.normalizeType'
99
+ },
100
+ secondCommitMutation: {
101
+ assertionLabel: 'second commit should be the "loadAll" mutation',
102
+ valueGetter: ({ commit }) => commit.mock.calls[1][0],
103
+ valueExpected: 'loadAll'
104
+ },
105
+ secondCommitParams: {
106
+ assertionLabel: 'second commit parameters should have a normalized type, ctx.state.config.namespace, data returned by request, and skipHaveAll set to false',
107
+ valueGetter: ({ commit }) => commit.mock.calls[1][1],
108
+ valueExpected: {
109
+ type: 'getters.normalizeType',
110
+ ctx: { state: { config: { namespace: 'unitTest' } } },
111
+ data: ['requestData'],
112
+ skipHaveAll: false,
113
+ },
114
+ assertionMethod: 'toMatchObject'
112
115
  },
113
- assertionMethod: 'toMatchObject'
114
- },
115
- countCommits: {
116
- assertionLabel: 'should only make two commits',
117
- valueGetter: ({ commit }) => commit.mock.calls,
118
- valueExpected: 2,
119
- assertionMethod: 'toHaveLength'
120
- }
121
-
122
- };
123
-
124
- describe('dashboard-store > actions > findAll', () => {
116
+ countCommits: {
117
+ assertionLabel: 'should only make two commits',
118
+ valueGetter: ({ commit }) => commit.mock.calls,
119
+ valueExpected: 2,
120
+ assertionMethod: 'toHaveLength'
121
+ }
122
+
123
+ };
124
+
125
125
  describe('called without a cache for the type in the second param', () => {
126
126
  const {
127
127
  dispatch, commit, getters, rootGetters, state
@@ -162,4 +162,89 @@ describe('dashboard-store: actions', () => {
162
162
  );
163
163
  });
164
164
  });
165
+
166
+ describe('findMatching', () => {
167
+ const setupContext = () => {
168
+ const commit = jest.fn();
169
+ const dispatch = jest.fn(() => 'dispatch');
170
+
171
+ const state = { config: { namespace: 'unitTest' } };
172
+ const getters = {
173
+ normalizeType: jest.fn((type) => type),
174
+ typeRegistered: jest.fn(() => true),
175
+ haveSelector: jest.fn(() => false),
176
+ matching: jest.fn(() => 'getters.all'),
177
+ urlFor: jest.fn(() => 'getters.urlFor'),
178
+ urlOptions: jest.fn(() => 'getters.urlOptions')
179
+ };
180
+ const rootGetters = { 'type-map/optionsFor': jest.fn() };
181
+
182
+ // we're not testing function output based off of state or getter inputs here since they are dependencies and should be tested independently
183
+ return {
184
+ state,
185
+ getters,
186
+ rootGetters,
187
+ commit,
188
+ dispatch
189
+ };
190
+ };
191
+ const genericType = 'services';
192
+ const genericSelector = 'a=b';
193
+ const genericOpt = {};
194
+
195
+ const assertionChain = [{
196
+ assertionLabel: 'Basic Selector',
197
+ input: {
198
+ type: genericType,
199
+ selector: genericSelector,
200
+ opt: { ...genericOpt },
201
+ namespace: undefined
202
+ },
203
+ output: {
204
+ getters: {
205
+ urlFor: [
206
+ genericType,
207
+ null,
208
+ {
209
+ ...genericOpt,
210
+ depaginate: undefined,
211
+ labelSelector: genericSelector,
212
+ url: 'getters.urlFor',
213
+ }
214
+ ]
215
+ },
216
+ actions: {
217
+ request: {
218
+ opt: {},
219
+ type: genericType
220
+ }
221
+ }
222
+ }
223
+ }];
224
+
225
+ const {
226
+ dispatch,
227
+ commit,
228
+ getters,
229
+ rootGetters,
230
+ state
231
+ } = setupContext();
232
+
233
+ it.each(assertionChain)(
234
+ '$assertionLabel',
235
+ async({ input, output }) => {
236
+ await findMatching(
237
+ {
238
+ dispatch,
239
+ getters,
240
+ rootGetters,
241
+ state,
242
+ commit,
243
+ },
244
+ input
245
+ );
246
+ expect(getters.urlFor).toHaveBeenCalledWith(...output.getters.urlFor);
247
+ }
248
+ );
249
+ });
165
250
  });
@@ -7,6 +7,7 @@ import { classify } from '@shell/plugins/dashboard-store/classify';
7
7
  import { normalizeType } from './normalize';
8
8
  import garbageCollect from '@shell/utils/gc/gc';
9
9
  import { addSchemaIndexFields } from '@shell/plugins/steve/schema.utils';
10
+ import { addParam } from '@shell/utils/url';
10
11
 
11
12
  export const _ALL = 'all';
12
13
  export const _MERGE = 'merge';
@@ -208,12 +209,12 @@ export default {
208
209
 
209
210
  const pageFetchOpts = {
210
211
  ...opt,
211
- url: `${ opt.url }?limit=${ opt.incremental }`
212
+ url: addParam(opt.url, 'limit', `${ opt.incremental }`),
212
213
  };
213
214
 
214
215
  // this is where we "hijack" the limit for the dispatch('request') some lines below
215
216
  // and therefore have 2 initial requests in parallel
216
- opt.url = `${ opt.url }?limit=100`;
217
+ opt.url = addParam(opt.url, 'limit', '100');
217
218
  skipHaveAll = true;
218
219
 
219
220
  // since we are forcing a request, clear the haveAll
@@ -368,10 +369,7 @@ export default {
368
369
  const typeOptions = rootGetters['type-map/optionsFor'](type);
369
370
 
370
371
  opt = opt || {};
371
-
372
- opt.filter = opt.filter || {};
373
- opt.filter['labelSelector'] = selector;
374
-
372
+ opt.labelSelector = selector;
375
373
  opt.url = getters.urlFor(type, null, opt);
376
374
  opt.depaginate = typeOptions?.depaginate;
377
375
 
@@ -1,5 +1,5 @@
1
1
 
2
- import { SCHEMA } from '@shell/config/types';
2
+ import { SCHEMA, COUNT } from '@shell/config/types';
3
3
 
4
4
  import { matches } from '@shell/utils/selector';
5
5
  import { typeMunge, typeRef, SIMPLE_TYPES } from '@shell/utils/create-yaml';
@@ -45,6 +45,29 @@ export const urlFor = (state, getters) => (type, id, opt) => {
45
45
  return url;
46
46
  };
47
47
 
48
+ /**
49
+ * Find the number of resources given
50
+ * - if the type is namespaced
51
+ * - if there are any counts per namespace
52
+ * - if there are no namespaces
53
+ * - if there is no total count
54
+ */
55
+ function matchingCounts(typeObj, namespaces) {
56
+ // That was easy
57
+ if ( !typeObj.namespaced || !typeObj.byNamespace || namespaces === null || typeObj.count === null) {
58
+ return typeObj.count;
59
+ }
60
+
61
+ let out = 0;
62
+
63
+ // Otherwise start with 0 and count up
64
+ for ( const namespace of namespaces ) {
65
+ out += typeObj.byNamespace[namespace]?.count || 0;
66
+ }
67
+
68
+ return out;
69
+ }
70
+
48
71
  export default {
49
72
 
50
73
  all: (state, getters, rootState) => (type) => {
@@ -345,5 +368,40 @@ export default {
345
368
 
346
369
  gcIgnoreTypes: () => {
347
370
  return {};
348
- }
371
+ },
372
+
373
+ /**
374
+ * For the given type, and it's settings, find the number of resources associated with it
375
+ *
376
+ * This takes into account if the type is namespaced.
377
+ *
378
+ * @param typeObj see inners for properties. must have at least `name` (resource type)
379
+ *
380
+ */
381
+ count: (state, getters, rootState, rootGetters) => (typeObj) => {
382
+ let _typeObj = typeObj;
383
+ const { name: type, count } = _typeObj;
384
+
385
+ if (!type) {
386
+ throw new Error(`Resource type required to calc count: ${ JSON.stringify(typeObj) }`);
387
+ }
388
+
389
+ if (!count) {
390
+ const schema = getters.schemaFor(type);
391
+ const counts = getters.all(COUNT)?.[0]?.counts || {};
392
+ const count = counts[type];
393
+
394
+ _typeObj = {
395
+ count: count ? count.summary.count || 0 : null,
396
+ byNamespace: count ? count.namespaces : {},
397
+ revision: count ? count.revision : null,
398
+ namespaced: schema?.attributes?.namespaced
399
+ };
400
+ }
401
+
402
+ const namespaces = Object.keys(rootGetters.activeNamespaceCache || {});
403
+
404
+ return matchingCounts(_typeObj, namespaces.length ? namespaces : null);
405
+ },
406
+
349
407
  };
@@ -1381,7 +1381,7 @@ export default class Resource {
1381
1381
 
1382
1382
  async download() {
1383
1383
  const value = await this.followLink('view', { headers: { accept: 'application/yaml' } });
1384
- const data = await this.$dispatch('cleanForDownload', value.data);
1384
+ const data = await this.cleanForDownload(value.data);
1385
1385
 
1386
1386
  downloadFile(`${ this.nameDisplay }.yaml`, data, 'application/yaml');
1387
1387
  }
@@ -1404,7 +1404,7 @@ export default class Resource {
1404
1404
  await eachLimit(items, 10, (item, idx) => {
1405
1405
  return item.followLink('view', { headers: { accept: 'application/yaml' } } ).then(async(data) => {
1406
1406
  const yaml = data.data || data;
1407
- const cleanedYaml = await this.$dispatch('cleanForDownload', yaml);
1407
+ const cleanedYaml = await this.cleanForDownload(yaml);
1408
1408
 
1409
1409
  files[`resources/${ names[idx] }`] = cleanedYaml;
1410
1410
  });
@@ -1481,6 +1481,10 @@ export default class Resource {
1481
1481
  this.$dispatch(`cleanForDiff`, this.toJSON());
1482
1482
  }
1483
1483
 
1484
+ async cleanForDownload(yaml) {
1485
+ return this.$dispatch(`cleanForDownload`, yaml);
1486
+ }
1487
+
1484
1488
  yamlForSave(yaml) {
1485
1489
  try {
1486
1490
  const obj = jsyaml.load(yaml);
@@ -37,6 +37,7 @@ describe('steve: getters', () => {
37
37
  expect(urlForGetter('typeFoo', undefined, { namespaced: ['nsBar', 'nsBaz'] })).toBe('protocol/urlFoo');
38
38
  });
39
39
  });
40
+
40
41
  describe('steve > getters > urlOptions', () => {
41
42
  // we're not testing function output based off of state or getter inputs here since they are dependencies
42
43
  const state = { config: { baseUrl: 'protocol' } };
@@ -71,12 +72,21 @@ describe('steve: getters', () => {
71
72
  it('returns a string with a single filter statement applied and formatted for steve if a single filter statement is applied and the url starts with "/v1"', () => {
72
73
  expect(urlOptionsGetter('/v1/foo', { filter: { bar: 'baz' } })).toBe('/v1/foo?filter=bar=baz&exclude=metadata.managedFields');
73
74
  });
75
+ it('returns a string with a single filter statement applied and formatted for steve if a single filter statement is applied and the url starts with "/k8s/clusters/c-m-n4x45x4b/v1/"', () => {
76
+ expect(urlOptionsGetter('/k8s/clusters/c-m-n4x45x4b/v1/foo', { filter: { bar: 'baz' } })).toBe('/k8s/clusters/c-m-n4x45x4b/v1/foo?filter=bar=baz&exclude=metadata.managedFields');
77
+ });
74
78
  it('returns a string with a multiple filter statements applied if a single filter statement is applied', () => {
75
79
  expect(urlOptionsGetter('foo', { filter: { bar: 'baz', far: 'faz' } })).toBe('foo?bar=baz&far=faz');
76
80
  });
77
81
  it('returns a string with a multiple filter statements applied and formatted for steve if a single filter statement is applied and the url starts with "/v1"', () => {
78
82
  expect(urlOptionsGetter('/v1/foo', { filter: { bar: 'baz', far: 'faz' } })).toBe('/v1/foo?filter=bar=baz&far=faz&exclude=metadata.managedFields');
79
83
  });
84
+ it('returns a string with a labelSelector and formatted for steve if the url starts with "/v1"', () => {
85
+ expect(urlOptionsGetter('/v1/foo', { labelSelector: 'a=b' })).toBe('/v1/foo?labelSelector=a=b&exclude=metadata.managedFields');
86
+ });
87
+ it('returns a string with a labelSelector and filter, and formatted for steve if the url starts with "/v1"', () => {
88
+ expect(urlOptionsGetter('/v1/foo', { labelSelector: 'a=b', filter: { bar: 'baz', far: 'faz' } })).toBe('/v1/foo?labelSelector=a=b&filter=bar=baz&far=faz&exclude=metadata.managedFields');
89
+ });
80
90
  it('returns a string with an exclude statement for "bar" and "metadata.managedFields" if excludeFields is a single element array with the string "bar" and the url starts with "/v1/"', () => {
81
91
  expect(urlOptionsGetter('/v1/foo', { excludeFields: ['bar'] })).toBe('/v1/foo?exclude=bar&exclude=metadata.managedFields');
82
92
  });
@@ -0,0 +1,159 @@
1
+ import { steveCleanForDownload } from '@shell/plugins/steve/resource-utils';
2
+
3
+ describe('steve: ressource-utils', () => {
4
+ it('should do nothing if the yaml is not passed', () => {
5
+ const r = steveCleanForDownload();
6
+
7
+ expect(r).toBeUndefined();
8
+ });
9
+ it('should remove all default rootKeys', () => {
10
+ const expectedYamlStr = `apiVersion: v1
11
+ kind: ConfigMap
12
+ metadata:
13
+ name: my-configmap
14
+ `;
15
+ const yamlStr = `
16
+ id: test_id
17
+ links:
18
+ view: https://example.com2
19
+ type: test_type
20
+ actions:
21
+ remove: https://example.com
22
+ ${ expectedYamlStr }
23
+ `;
24
+ const cleanedYamlStr = steveCleanForDownload(yamlStr);
25
+
26
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
27
+ });
28
+ it('should remove all the specified root keys', () => {
29
+ const part = `apiVersion: v1
30
+ kind: Secret
31
+ metadata:
32
+ name: my-secret`;
33
+
34
+ const rootKeyToYamlStringMap = {
35
+ id: 'id: test_id',
36
+ links: `links:
37
+ view: https://example.com`,
38
+ actions: `actions:
39
+ remove: https://example.com`,
40
+ type: 'type: Opaque'
41
+ };
42
+
43
+ const entries = Object.entries(rootKeyToYamlStringMap);
44
+ const yamlStr = `${ part }
45
+ ${ entries.map(([_, str]) => str).join('\n') }`;
46
+
47
+ entries.forEach(([key, str]) => {
48
+ const expectedYamlStr = `${ part }
49
+ ${ entries.filter(([k]) => k !== key).map(([_, str]) => str).join('\n') }\n`;
50
+ const cleanedYamlStr = steveCleanForDownload(yamlStr, { rootKeys: [key] });
51
+
52
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
53
+ });
54
+ });
55
+ it('should remove all default metadata keys', () => {
56
+ const expectedYamlStr = `apiVersion: v1
57
+ kind: ConfigMap
58
+ metadata:
59
+ name: my-configmap
60
+ `;
61
+ const yamlStr = `apiVersion: v1
62
+ kind: ConfigMap
63
+ metadata:
64
+ name: my-configmap
65
+ fields:
66
+ - kube-root-ca.crt
67
+ - 1
68
+ - 7d23h
69
+ relationships:
70
+ - rel: 'owner'
71
+ state: 'active'
72
+ `;
73
+ const cleanedYamlStr = steveCleanForDownload(yamlStr);
74
+
75
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
76
+ });
77
+
78
+ it('should remove all the specified metadata keys', () => {
79
+ const part = `apiVersion: v1
80
+ kind: ConfigMap
81
+ metadata:
82
+ name: my-configmap`;
83
+
84
+ const metadataKeyToYamlStringMap = {
85
+ fields:
86
+ ` fields:
87
+ - kube-root-ca.crt
88
+ - 1
89
+ - 7d23h`,
90
+ relationships:
91
+ ` relationships:
92
+ - rel: owner`,
93
+ state: ` state: active`
94
+ };
95
+
96
+ const entries = Object.entries(metadataKeyToYamlStringMap);
97
+ const yamlStr = `${ part }
98
+ ${ entries.map(([_, str]) => str).join('\n') }`;
99
+
100
+ entries.forEach(([key, str]) => {
101
+ const expectedYamlStr = `${ part }
102
+ ${ entries.filter(([k]) => k !== key).map(([_, str]) => str).join('\n') }\n`;
103
+ const cleanedYamlStr = steveCleanForDownload(yamlStr, { metadataKeys: [key] });
104
+
105
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
106
+ });
107
+ });
108
+ it('should remove all defalut condition keys', () => {
109
+ const expectedYamlStr = `apiVersion: v1
110
+ kind: ConfigMap
111
+ metadata:
112
+ name: my-configmap
113
+ status:
114
+ conditions:
115
+ - {}
116
+ - {}
117
+ - message: message
118
+ `;
119
+ const yamlStr = `apiVersion: v1
120
+ kind: ConfigMap
121
+ metadata:
122
+ name: my-configmap
123
+ status:
124
+ conditions:
125
+ - error: 'error'
126
+ - transitioning: false
127
+ - message: message
128
+ `;
129
+ const cleanedYamlStr = steveCleanForDownload(yamlStr);
130
+
131
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
132
+ });
133
+ it('should remove all the specified condition keys', () => {
134
+ const part = `apiVersion: v1
135
+ kind: ConfigMap
136
+ metadata:
137
+ name: my-configmap
138
+ status:
139
+ conditions:
140
+ - message: message`;
141
+
142
+ const conditionKeyToYamlStringMap = {
143
+ error: ' - error: error',
144
+ transitioning: ' - transitioning: false'
145
+ };
146
+
147
+ const entries = Object.entries(conditionKeyToYamlStringMap);
148
+ const yamlStr = `${ part }
149
+ ${ entries.map(([_, str]) => str).join('\n') }`;
150
+
151
+ entries.forEach(([key, str]) => {
152
+ const expectedYamlStr = `${ part }
153
+ ${ entries.map(([k, str]) => k === key ? ' - {}' : str).join('\n') }\n`;
154
+ const cleanedYamlStr = steveCleanForDownload(yamlStr, { conditionKeys: [key] });
155
+
156
+ expect(cleanedYamlStr).toBe(expectedYamlStr);
157
+ });
158
+ });
159
+ });