@rancher/shell 3.0.5-rc.3 → 3.0.5-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/assets/images/icons/document.svg +3 -0
  2. package/assets/images/vendor/cognito.svg +1 -0
  3. package/assets/styles/app.scss +1 -0
  4. package/assets/styles/base/_basic.scss +10 -0
  5. package/assets/styles/base/_spacing.scss +29 -0
  6. package/assets/styles/global/_layout.scss +1 -1
  7. package/assets/styles/themes/_dark.scss +25 -0
  8. package/assets/styles/themes/_light.scss +65 -0
  9. package/assets/translations/en-us.yaml +322 -24
  10. package/assets/translations/zh-hans.yaml +8 -5
  11. package/components/Certificates.vue +5 -0
  12. package/components/FilterPanel.vue +156 -0
  13. package/components/{fleet/ForceDirectedTreeChart/index.vue → ForceDirectedTreeChart.vue} +47 -41
  14. package/components/IconOrSvg.vue +14 -35
  15. package/components/PromptRemove.vue +5 -1
  16. package/components/Resource/Detail/Card/PodsCard/Bubble.vue +13 -0
  17. package/components/Resource/Detail/Card/PodsCard/composable.ts +30 -0
  18. package/components/Resource/Detail/Card/PodsCard/index.vue +118 -0
  19. package/components/Resource/Detail/Card/ResourceUsageCard/composable.ts +51 -0
  20. package/components/Resource/Detail/Card/ResourceUsageCard/index.vue +79 -0
  21. package/components/Resource/Detail/Card/Scaler.vue +89 -0
  22. package/components/Resource/Detail/Card/StateCard/composables.ts +112 -0
  23. package/components/Resource/Detail/Card/StateCard/index.vue +39 -0
  24. package/components/Resource/Detail/Card/VerticalGap.vue +11 -0
  25. package/components/Resource/Detail/Card/__tests__/Card.test.ts +36 -0
  26. package/components/Resource/Detail/Card/__tests__/PodsCard.test.ts +84 -0
  27. package/components/Resource/Detail/Card/__tests__/ResourceUsageCard.test.ts +72 -0
  28. package/components/Resource/Detail/Card/__tests__/Scaler.test.ts +87 -0
  29. package/components/Resource/Detail/Card/__tests__/StateCard.test.ts +53 -0
  30. package/components/Resource/Detail/Card/__tests__/VerticalGap.test.ts +14 -0
  31. package/components/Resource/Detail/Card/__tests__/index.test.ts +36 -0
  32. package/components/Resource/Detail/Card/index.vue +56 -0
  33. package/components/Resource/Detail/Metadata/Annotations/__tests__/index.test.ts +19 -0
  34. package/components/Resource/Detail/Metadata/Annotations/composable.ts +12 -0
  35. package/components/Resource/Detail/Metadata/Annotations/index.vue +26 -0
  36. package/components/Resource/Detail/Metadata/IdentifyingInformation/__tests__/index.test.ts +103 -0
  37. package/components/Resource/Detail/Metadata/IdentifyingInformation/composable.ts +281 -0
  38. package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +111 -0
  39. package/components/Resource/Detail/Metadata/KeyValue.vue +130 -0
  40. package/components/Resource/Detail/Metadata/Labels/__tests__/index.test.ts +18 -0
  41. package/components/Resource/Detail/Metadata/Labels/composable.ts +12 -0
  42. package/components/Resource/Detail/Metadata/Labels/index.vue +27 -0
  43. package/components/Resource/Detail/Metadata/Rectangle.vue +32 -0
  44. package/components/Resource/Detail/Metadata/__tests__/KeyValue.test.ts +107 -0
  45. package/components/Resource/Detail/Metadata/__tests__/Rectangle.test.ts +24 -0
  46. package/components/Resource/Detail/Metadata/__tests__/index.test.ts +91 -0
  47. package/components/Resource/Detail/Metadata/composables.ts +29 -0
  48. package/components/Resource/Detail/Metadata/index.vue +66 -0
  49. package/components/Resource/Detail/Page.vue +22 -0
  50. package/components/Resource/Detail/PercentageBar.vue +40 -0
  51. package/components/Resource/Detail/ResourceRow.vue +119 -0
  52. package/components/Resource/Detail/SpacedRow.vue +14 -0
  53. package/components/Resource/Detail/StatusBar.vue +59 -0
  54. package/components/Resource/Detail/StatusRow.vue +61 -0
  55. package/components/Resource/Detail/TitleBar/Title.vue +13 -0
  56. package/components/Resource/Detail/TitleBar/Top.vue +14 -0
  57. package/components/Resource/Detail/TitleBar/__tests__/Title.test.ts +17 -0
  58. package/components/Resource/Detail/TitleBar/__tests__/Top.test.ts +17 -0
  59. package/components/Resource/Detail/TitleBar/__tests__/index.test.ts +142 -0
  60. package/components/Resource/Detail/TitleBar/composable.ts +31 -0
  61. package/components/Resource/Detail/TitleBar/index.vue +124 -0
  62. package/components/Resource/Detail/Top/index.vue +34 -0
  63. package/components/Resource/Detail/__tests__/Page.test.ts +32 -0
  64. package/components/ResourceDetail/__tests__/index.test.ts +114 -0
  65. package/components/ResourceDetail/index.vue +64 -562
  66. package/components/ResourceDetail/legacy.vue +545 -0
  67. package/components/ResourceTable.vue +41 -7
  68. package/components/SlideInPanelManager.vue +76 -8
  69. package/components/SortableTable/index.vue +13 -2
  70. package/components/SortableTable/selection.js +21 -8
  71. package/components/StatusBadge.vue +6 -4
  72. package/components/SubtleLink.vue +25 -0
  73. package/components/Wizard.vue +12 -1
  74. package/components/YamlEditor.vue +1 -1
  75. package/components/__tests__/FilterPanel.test.ts +81 -0
  76. package/components/auth/AuthBanner.vue +2 -3
  77. package/components/auth/RoleDetailEdit.vue +45 -3
  78. package/components/auth/login/oidc.vue +6 -1
  79. package/components/fleet/FleetApplications.vue +181 -0
  80. package/components/fleet/FleetHelmOps.vue +115 -0
  81. package/components/fleet/FleetIntro.vue +58 -28
  82. package/components/fleet/FleetNoWorkspaces.vue +5 -1
  83. package/components/fleet/FleetOCIStorageSecret.vue +171 -0
  84. package/components/fleet/FleetRepos.vue +38 -76
  85. package/components/fleet/FleetResources.vue +50 -22
  86. package/components/fleet/FleetSummary.vue +26 -51
  87. package/components/fleet/__tests__/FleetOCIStorageSecret.test.ts +213 -0
  88. package/components/fleet/__tests__/FleetSummary.test.ts +39 -39
  89. package/components/fleet/dashboard/Empty.vue +73 -0
  90. package/components/fleet/dashboard/ResourceCard.vue +183 -0
  91. package/components/fleet/dashboard/ResourceCardSummary.vue +199 -0
  92. package/components/fleet/dashboard/ResourceDetails.vue +196 -0
  93. package/components/fleet/dashboard/ResourcePanel.vue +376 -0
  94. package/components/form/ArrayList.vue +6 -0
  95. package/components/form/SimpleSecretSelector.vue +8 -2
  96. package/components/form/ValueFromResource.vue +31 -19
  97. package/components/formatter/FleetApplicationClustersReady.vue +77 -0
  98. package/components/formatter/FleetApplicationSource.vue +71 -0
  99. package/components/formatter/FleetSummaryGraph.vue +7 -0
  100. package/components/nav/Header.vue +8 -7
  101. package/components/nav/TopLevelMenu.helper.ts +55 -34
  102. package/components/nav/TopLevelMenu.vue +11 -0
  103. package/components/nav/Type.vue +4 -1
  104. package/composables/useI18n.ts +12 -11
  105. package/config/labels-annotations.js +14 -11
  106. package/config/product/auth.js +1 -0
  107. package/config/product/fleet.js +70 -17
  108. package/config/query-params.js +3 -1
  109. package/config/roles.ts +1 -0
  110. package/config/router/routes.js +20 -2
  111. package/config/secret.ts +15 -0
  112. package/config/settings.ts +3 -2
  113. package/config/table-headers.js +52 -22
  114. package/config/types.js +2 -0
  115. package/core/plugin-helpers.ts +3 -2
  116. package/detail/fleet.cattle.io.cluster.vue +28 -15
  117. package/detail/fleet.cattle.io.gitrepo.vue +10 -1
  118. package/detail/fleet.cattle.io.helmop.vue +157 -0
  119. package/dialog/HelmOpForceUpdateDialog.vue +132 -0
  120. package/dialog/RedeployWorkloadDialog.vue +164 -0
  121. package/edit/__tests__/fleet.cattle.io.gitrepo.test.ts +56 -67
  122. package/edit/auth/oidc.vue +159 -93
  123. package/edit/fleet.cattle.io.gitrepo.vue +26 -33
  124. package/edit/fleet.cattle.io.helmop.vue +997 -0
  125. package/edit/management.cattle.io.fleetworkspace.vue +43 -10
  126. package/list/fleet.cattle.io.gitrepo.vue +1 -1
  127. package/list/fleet.cattle.io.helmop.vue +108 -0
  128. package/list/namespace.vue +5 -2
  129. package/mixins/auth-config.js +8 -1
  130. package/mixins/preset.js +100 -0
  131. package/mixins/resource-fetch-api-pagination.js +2 -0
  132. package/mixins/resource-fetch.js +1 -1
  133. package/mixins/resource-table-watch.js +45 -0
  134. package/models/__tests__/chart.test.ts +273 -0
  135. package/models/__tests__/fleet.cattle.io.gitrepo.test.ts +1 -1
  136. package/models/chart.js +144 -2
  137. package/models/fleet-application.js +385 -0
  138. package/models/fleet.cattle.io.bundle.js +9 -8
  139. package/models/fleet.cattle.io.gitrepo.js +41 -365
  140. package/models/fleet.cattle.io.helmop.js +228 -0
  141. package/models/management.cattle.io.authconfig.js +1 -0
  142. package/models/management.cattle.io.fleetworkspace.js +12 -0
  143. package/models/workload.js +14 -18
  144. package/package.json +2 -1
  145. package/pages/auth/verify.vue +13 -1
  146. package/pages/c/_cluster/apps/charts/AddRepoLink.vue +37 -0
  147. package/pages/c/_cluster/apps/charts/AppChartCardFooter.vue +80 -0
  148. package/pages/c/_cluster/apps/charts/AppChartCardSubHeader.vue +54 -0
  149. package/pages/c/_cluster/apps/charts/StatusLabel.vue +33 -0
  150. package/pages/c/_cluster/apps/charts/index.vue +302 -484
  151. package/pages/c/_cluster/explorer/EventsTable.vue +1 -1
  152. package/pages/c/_cluster/fleet/__tests__/index.test.ts +426 -0
  153. package/pages/c/_cluster/fleet/application/_resource/_id.vue +14 -0
  154. package/pages/c/_cluster/fleet/application/_resource/create.vue +14 -0
  155. package/pages/c/_cluster/fleet/application/create.vue +340 -0
  156. package/pages/c/_cluster/fleet/application/index.vue +139 -0
  157. package/pages/c/_cluster/fleet/graph/config.js +277 -0
  158. package/pages/c/_cluster/fleet/index.vue +772 -330
  159. package/pages/explorer/resource/detail/configmap.vue +19 -0
  160. package/plugins/dashboard-store/actions.js +31 -9
  161. package/plugins/dashboard-store/getters.js +34 -21
  162. package/plugins/dashboard-store/mutations.js +51 -7
  163. package/plugins/dashboard-store/resource-class.js +14 -2
  164. package/plugins/steve/__tests__/subscribe.spec.ts +66 -1
  165. package/plugins/steve/actions.js +3 -0
  166. package/plugins/steve/steve-pagination-utils.ts +14 -13
  167. package/plugins/steve/subscribe.js +229 -42
  168. package/rancher-components/BadgeState/BadgeState.vue +3 -1
  169. package/rancher-components/Form/Checkbox/Checkbox.vue +2 -2
  170. package/rancher-components/RcItemCard/RcItemCard.test.ts +189 -0
  171. package/rancher-components/RcItemCard/RcItemCard.vue +425 -0
  172. package/rancher-components/RcItemCard/RcItemCardAction.vue +24 -0
  173. package/rancher-components/RcItemCard/index.ts +2 -0
  174. package/store/auth.js +1 -0
  175. package/store/catalog.js +62 -24
  176. package/store/index.js +33 -14
  177. package/store/slideInPanel.ts +6 -0
  178. package/store/type-map.js +1 -0
  179. package/types/fleet.d.ts +35 -0
  180. package/types/resources/settings.d.ts +19 -1
  181. package/types/shell/index.d.ts +339 -272
  182. package/types/store/dashboard-store.types.ts +17 -3
  183. package/types/store/pagination.types.ts +6 -1
  184. package/types/store/subscribe.types.ts +50 -0
  185. package/utils/auth.js +32 -3
  186. package/utils/fleet-types.ts +0 -0
  187. package/utils/fleet.ts +200 -1
  188. package/utils/pagination-utils.ts +26 -1
  189. package/utils/pagination-wrapper.ts +132 -50
  190. package/utils/settings.ts +4 -1
  191. package/utils/style.ts +39 -0
  192. package/utils/validators/formRules/__tests__/index.test.ts +36 -3
  193. package/utils/validators/formRules/index.ts +10 -3
  194. package/utils/window.js +11 -7
  195. package/components/__tests__/ApplicationCard.test.ts +0 -27
  196. package/components/cards/ApplicationCard.vue +0 -145
  197. package/components/fleet/ForceDirectedTreeChart/chartIcons.js +0 -17
  198. package/config/secret.js +0 -14
  199. package/pages/c/_cluster/fleet/GitRepoGraphConfig.js +0 -249
  200. /package/{components/form/SSHKnownHosts → dialog}/__tests__/KnownHostsEditDialog.test.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { PaginationArgs } from '@shell/types/store/pagination.types';
1
+ import { PaginationArgs, StorePagination } from '@shell/types/store/pagination.types';
2
2
 
3
3
  /**
4
4
  * Properties on all findX actions
@@ -47,6 +47,11 @@ export interface ActionFindAllArgs extends ActionCoreFindArgs {
47
47
  depaginate?: boolean,
48
48
  }
49
49
 
50
+ export interface ActionFindPageTransientResult<T> {
51
+ pagination: StorePagination,
52
+ data: T[],
53
+ }
54
+
50
55
  /**
51
56
  * Args used for findPage action
52
57
  */
@@ -62,8 +67,17 @@ export interface ActionFindPageArgs extends ActionCoreFindArgs {
62
67
  */
63
68
  namespaced?: string,
64
69
  /**
65
- * Result of request is transient and not persisted to store
70
+ * Watch for changes
71
+ *
72
+ * false = no, all other values = yes
73
+ */
74
+ watch?: boolean,
75
+ /**
76
+ * Does this request stem from a list with manual refresh?
66
77
  */
67
- transient?: boolean,
68
78
  hasManualRefresh?: boolean,
79
+ /**
80
+ * If true don't persist the http response to the store, just pass it back
81
+ */
82
+ transient?: boolean,
69
83
  }
@@ -451,7 +451,12 @@ export interface StorePaginationRequest {
451
451
  /**
452
452
  * The set of pagination args used to create the request
453
453
  */
454
- pagination: PaginationArgs
454
+ pagination: PaginationArgs,
455
+
456
+ /**
457
+ * Does this request stem from a list with manual refresh?
458
+ */
459
+ hasManualRefresh?: boolean,
455
460
  }
456
461
 
457
462
  /**
@@ -0,0 +1,50 @@
1
+ /* eslint-disable no-unused-vars */
2
+ export enum STEVE_WATCH_MODE {
3
+ DEFAULT = '',
4
+ RESOURCE_CHANGES = 'resource.changes'
5
+ }
6
+ /* eslint-enable no-unused-vars */
7
+
8
+ /* eslint-disable no-unused-vars */
9
+ export enum STEVE_WATCH_EVENT {
10
+ START = 'resource.start',
11
+ CREATE = 'resource.create',
12
+ CHANGE = 'resource.change',
13
+ CHANGES = 'resource.changes',
14
+ REMOVE = 'resource.resource.remove',
15
+ ERROR = 'resource.error',
16
+ STOP = 'resource.stop',
17
+ }
18
+ /* eslint-enable no-unused-vars */
19
+
20
+ export interface STEVE_WATCH_PARAMS {
21
+ type: string,
22
+ selector?: string,
23
+ id?: string,
24
+ revision?: string,
25
+ namespace?: string,
26
+ stop?: boolean,
27
+ force?: boolean,
28
+ mode?: STEVE_WATCH_MODE
29
+ }
30
+
31
+ export type STEVE_WATCH_EVENT_LISTENER_CALLBACK = () => void
32
+ export interface STEVE_WATCH_EVENT_LISTENER {
33
+ params: STEVE_WATCH_PARAMS,
34
+ callbacks: { [id: string]: STEVE_WATCH_EVENT_LISTENER_CALLBACK},
35
+ }
36
+
37
+ export interface STEVE_WATCH_EVENT_PARAMS_COMMON {
38
+ event: STEVE_WATCH_EVENT,
39
+ id: string,
40
+ /**
41
+ * of type @STEVE_WATCH_PARAMS
42
+ */
43
+ params: STEVE_WATCH_PARAMS,
44
+ }
45
+
46
+ export interface STEVE_WATCH_EVENT_PARAMS extends STEVE_WATCH_EVENT_PARAMS_COMMON {
47
+ callback: STEVE_WATCH_EVENT_LISTENER_CALLBACK,
48
+ }
49
+
50
+ export type STEVE_UNWATCH_EVENT_PARAMS = STEVE_WATCH_EVENT_PARAMS_COMMON
package/utils/auth.js CHANGED
@@ -9,6 +9,8 @@ import { getProductFromRoute, getResourceFromRoute } from '@shell/utils/router';
9
9
  import { NAME as EXPLORER } from '@shell/config/product/explorer';
10
10
  import { findBy } from '@shell/utils/array';
11
11
 
12
+ export const AUTH_BROADCAST_CHANNEL_NAME = 'rancher-auth-test-callback';
13
+
12
14
  export function openAuthPopup(url, provider) {
13
15
  const popup = new Popup(() => {
14
16
  popup.promise = new Promise((resolve, reject) => {
@@ -16,19 +18,46 @@ export function openAuthPopup(url, provider) {
16
18
  popup.reject = reject;
17
19
  });
18
20
 
21
+ const bc = new BroadcastChannel(AUTH_BROADCAST_CHANNEL_NAME);
22
+
19
23
  window.onAuthTest = (error, code) => {
20
24
  if (error) {
21
25
  popup.reject(error);
22
26
  }
23
27
 
28
+ bc.close();
24
29
  delete window.onAuthTest;
25
30
  popup.resolve(code);
26
31
  };
27
- }, () => {
28
- popup.reject(new Error('Access was not authorized'));
32
+
33
+ // Broadcast message listener for when the window can not invoke a method on the opener
34
+ bc.onmessage = (msgEvent) => {
35
+ try {
36
+ const obj = JSON.parse(msgEvent.data);
37
+ const { error, code } = obj;
38
+
39
+ window.onAuthTest(error, code);
40
+ } catch (e) {
41
+ window.onAuthTest(new Error(`Access was not authorized (invalid callback metadata)`));
42
+
43
+ console.error('Unable to process message from auth broadcast channel', e); // eslint-disable-line no-console
44
+ }
45
+ };
46
+ }, (e) => {
47
+ let detail = '';
48
+
49
+ // If there was an error and it has a message, add that to the message we send back via the promise
50
+ if (e?.type === 'error' && e?.message) {
51
+ detail = ` (${ e.message })`;
52
+ }
53
+
54
+ popup.reject(new Error(`Access was not authorized${ detail }`));
29
55
  });
30
56
 
31
- popup.open(url, 'auth-test', popupWindowOptions());
57
+ // So far, only Amazon Cognito sets the origin policy that prevents us from detecting when the popup is closed
58
+ const doNotPollForClosure = provider === 'cognito';
59
+
60
+ popup.open(url, 'auth-test', popupWindowOptions(), doNotPollForClosure);
32
61
 
33
62
  return popup.promise;
34
63
  }
File without changes
package/utils/fleet.ts CHANGED
@@ -5,9 +5,11 @@ import {
5
5
  BundleDeploymentStatus,
6
6
  Condition,
7
7
  } from '@shell/types/resources/fleet';
8
- import { mapStateToEnum, STATES_ENUM } from '@shell/plugins/dashboard-store/resource-class';
8
+ import { mapStateToEnum, STATES_ENUM, STATES } from '@shell/plugins/dashboard-store/resource-class';
9
9
  import { FLEET as FLEET_LABELS } from '@shell/config/labels-annotations';
10
10
  import { NAME as EXPLORER_NAME } from '@shell/config/product/explorer';
11
+ import { FleetDashboardState, FleetResourceState } from '@shell/types/fleet';
12
+ import { FLEET } from '@shell/config/types';
11
13
 
12
14
  interface Resource extends BundleDeploymentResource {
13
15
  state: string,
@@ -17,6 +19,17 @@ type Labels = {
17
19
  [key: string]: string,
18
20
  }
19
21
 
22
+ interface KeyRef {
23
+ key: string;
24
+ name: string;
25
+ namespace?: string;
26
+ }
27
+
28
+ interface ValueFrom {
29
+ configMapKeyRef?: KeyRef;
30
+ secretKeyRef?: KeyRef;
31
+ }
32
+
20
33
  function resourceKey(r: BundleResourceKey): string {
21
34
  return `${ r.kind }/${ r.namespace }/${ r.name }`;
22
35
  }
@@ -29,7 +42,140 @@ function conditionIsTrue(conditions: Condition[] | undefined, type: string): boo
29
42
  return !!conditions.find((c) => c.type === type && c.status.toLowerCase() === 'true');
30
43
  }
31
44
 
45
+ class HelmOp {
46
+ fromValuesFrom(data: ValueFrom[]): { valueFrom: ValueFrom }[] {
47
+ return (data || []).map((elem) => {
48
+ const out = {} as any;
49
+
50
+ const cm = elem.configMapKeyRef;
51
+
52
+ if (cm) {
53
+ out.valueFrom = {
54
+ configMapKeyRef: {
55
+ key: cm.key || '',
56
+ name: cm.name || '',
57
+ }
58
+ };
59
+ }
60
+
61
+ const sc = elem.secretKeyRef;
62
+
63
+ if (sc) {
64
+ out.valueFrom = {
65
+ secretKeyRef: {
66
+ key: sc.key || '',
67
+ name: sc.name || '',
68
+ }
69
+ };
70
+ }
71
+
72
+ return out;
73
+ });
74
+ }
75
+
76
+ toValuesFrom(data: { valueFrom: ValueFrom }[], namespace: string): ValueFrom[] {
77
+ return (data || [])
78
+ .filter((f) => f.valueFrom?.configMapKeyRef || f.valueFrom?.secretKeyRef)
79
+ .map(({ valueFrom }) => {
80
+ const cm = valueFrom.configMapKeyRef;
81
+ const sc = valueFrom.secretKeyRef;
82
+
83
+ const out = {} as ValueFrom;
84
+
85
+ if (cm?.name) {
86
+ out.configMapKeyRef = {
87
+ key: cm.key,
88
+ name: cm.name,
89
+ namespace
90
+ };
91
+ }
92
+
93
+ if (sc?.name) {
94
+ out.secretKeyRef = {
95
+ key: sc.key,
96
+ name: sc.name,
97
+ namespace
98
+ };
99
+ }
100
+
101
+ return out;
102
+ });
103
+ }
104
+ }
105
+
32
106
  class Fleet {
107
+ resourceIcons = {
108
+ [FLEET.GIT_REPO]: 'icon icon-github',
109
+ [FLEET.HELM_OP]: 'icon icon-helm',
110
+ };
111
+
112
+ dashboardIcons = {
113
+ [FLEET.GIT_REPO]: 'icon icon-git',
114
+ [FLEET.HELM_OP]: 'icon icon-helm',
115
+ };
116
+
117
+ dashboardStates: FleetDashboardState[] = [
118
+ {
119
+ index: 0,
120
+ id: 'error',
121
+ label: 'Error',
122
+ color: '#F64747',
123
+ icon: 'icon icon-error',
124
+ stateBackground: 'bg-error'
125
+ },
126
+ {
127
+ index: 1,
128
+ id: 'warning',
129
+ label: 'Warning',
130
+ color: '#DAC342',
131
+ icon: 'icon icon-warning',
132
+ stateBackground: 'bg-warning'
133
+ },
134
+ {
135
+ index: 2,
136
+ id: 'success',
137
+ label: 'Active',
138
+ color: '#5D995D',
139
+ icon: 'icon icon-checkmark',
140
+ stateBackground: 'bg-success'
141
+ },
142
+ {
143
+ index: 3,
144
+ id: 'info',
145
+ label: 'InProgress',
146
+ color: '#3d98d3',
147
+ icon: 'icon icon-warning',
148
+ stateBackground: 'bg-info'
149
+ },
150
+ ];
151
+
152
+ HelmOp = new HelmOp();
153
+
154
+ GIT_HTTPS_REGEX = /^https?:\/\/github\.com\/(.*?)(\.git)?\/*$/;
155
+ GIT_SSH_REGEX = /^git@github\.com:.*\.git$/;
156
+ HTTP_REGEX = /^(https?:\/\/[^\s]+)$/;
157
+ OCI_REGEX = /^oci:\/\//;
158
+
159
+ quacksLikeAHash(str: string) {
160
+ if (str.match(/^[a-f0-9]{40,}$/i)) {
161
+ return true;
162
+ }
163
+
164
+ return false;
165
+ }
166
+
167
+ parseSSHUrl(url: string) {
168
+ const parts = (url || '').split(':');
169
+
170
+ const sshUserAndHost = parts[0];
171
+ const repoPath = parts[1]?.replace('.git', '');
172
+
173
+ return {
174
+ sshUserAndHost,
175
+ repoPath
176
+ };
177
+ }
178
+
33
179
  resourceId(r: BundleResourceKey): string {
34
180
  return r.namespace ? `${ r.namespace }/${ r.name }` : r.name;
35
181
  }
@@ -126,6 +272,59 @@ class Fleet {
126
272
  return STATES_ENUM.READY;
127
273
  }
128
274
  }
275
+
276
+ getResourcesDefaultState(labelGetter: (key: string, args: any, fallback: any) => Record<string, any>, stateKey: string): Record<string, FleetResourceState> {
277
+ return [
278
+ STATES_ENUM.READY,
279
+ STATES_ENUM.NOT_READY,
280
+ STATES_ENUM.WAIT_APPLIED,
281
+ STATES_ENUM.MODIFIED,
282
+ STATES_ENUM.MISSING,
283
+ STATES_ENUM.ORPHANED,
284
+ STATES_ENUM.UNKNOWN,
285
+ ].reduce((acc: Record<string, any>, state) => {
286
+ acc[state] = {
287
+ count: 0,
288
+ color: STATES[state].color,
289
+ label: labelGetter(`${ stateKey }.${ state }`, null, STATES[state].label ),
290
+ status: state
291
+ };
292
+
293
+ return acc;
294
+ }, {});
295
+ }
296
+
297
+ getBundlesDefaultState(labelGetter: (key: string, args: any, fallback: any) => Record<string, any>, stateKey: string): Record<string, FleetResourceState> {
298
+ return [
299
+ STATES_ENUM.READY,
300
+ STATES_ENUM.INFO,
301
+ STATES_ENUM.WARNING,
302
+ STATES_ENUM.NOT_READY,
303
+ STATES_ENUM.ERROR,
304
+ STATES_ENUM.ERR_APPLIED,
305
+ STATES_ENUM.WAIT_APPLIED,
306
+ STATES_ENUM.UNKNOWN,
307
+ ].reduce((acc: Record<string, any>, state) => {
308
+ acc[state] = {
309
+ count: 0,
310
+ color: STATES[state].color,
311
+ label: labelGetter(`${ stateKey }.${ state }`, null, STATES[state].label ),
312
+ status: state
313
+ };
314
+
315
+ return acc;
316
+ }, {});
317
+ }
318
+
319
+ getDashboardStateId(resource: { stateColor: string }): string {
320
+ return resource?.stateColor?.replace('text-', '') || 'warning';
321
+ }
322
+
323
+ getDashboardState(resource: { stateColor: string }): FleetDashboardState | {} {
324
+ const stateId = this.getDashboardStateId(resource);
325
+
326
+ return this.dashboardStates.find(({ id }) => stateId === id) || {};
327
+ }
129
328
  }
130
329
 
131
330
  const instance = new Fleet();
@@ -1,4 +1,4 @@
1
- import { PaginationSettings, PaginationSettingsStore } from '@shell/types/resources/settings';
1
+ import { PaginationFeature, PaginationSettings, PaginationSettingsStore } from '@shell/types/resources/settings';
2
2
  import {
3
3
  NAMESPACE_FILTER_ALL_USER as ALL_USER,
4
4
  NAMESPACE_FILTER_ALL as ALL,
@@ -128,6 +128,31 @@ class PaginationUtils {
128
128
  return false;
129
129
  }
130
130
 
131
+ listAutoRefreshToggleEnabled({ rootGetters }: any): boolean {
132
+ return this.isFeatureEnabled({ rootGetters }, 'listAutoRefreshToggle');
133
+ }
134
+
135
+ isListManualRefreshEnabled({ rootGetters }: any): boolean {
136
+ return this.isFeatureEnabled({ rootGetters }, 'listManualRefresh');
137
+ }
138
+
139
+ private isFeatureEnabled({ rootGetters }: any, featureName: PaginationFeature): boolean {
140
+ // Cache must be enabled to support pagination api
141
+ if (!this.isSteveCacheEnabled({ rootGetters })) {
142
+ return false;
143
+ }
144
+
145
+ const settings = this.getSettings({ rootGetters });
146
+
147
+ return !!settings.features?.[featureName]?.enabled;
148
+ }
149
+
150
+ resourceChangesDebounceMs({ rootGetters }: any): number | undefined {
151
+ const settings = this.getSettings({ rootGetters });
152
+
153
+ return settings.resourceChangesDebounceMs;
154
+ }
155
+
131
156
  validateNsProjectFilters(nsProjectFilters: string[]) {
132
157
  return nsProjectFilters?.every((f) => this.validateNsProjectFilter(f));
133
158
  }
@@ -1,70 +1,152 @@
1
1
  import paginationUtils from '@shell/utils/pagination-utils';
2
- import { PaginationArgs, PaginationResourceContext, StorePagination } from '@shell/types/store/pagination.types';
2
+ import { PaginationArgs, PaginationResourceContext } from '@shell/types/store/pagination.types';
3
3
  import { VuexStore } from '@shell/types/store/vuex';
4
- import { ActionFindPageArgs } from '@shell/types/store/dashboard-store.types';
4
+ import { ActionFindPageArgs, ActionFindPageTransientResult } from '@shell/types/store/dashboard-store.types';
5
+ import {
6
+ STEVE_WATCH_EVENT_LISTENER_CALLBACK, STEVE_UNWATCH_EVENT_PARAMS, STEVE_WATCH_EVENT, STEVE_WATCH_EVENT_PARAMS, STEVE_WATCH_EVENT_PARAMS_COMMON, STEVE_WATCH_MODE
7
+ } from '@shell/types/store/subscribe.types';
8
+ import { Reactive, reactive } from 'vue';
5
9
 
6
- interface Result<T> {
7
- data: Array<T>
8
- pagination: StorePagination
10
+ interface Args {
11
+ $store: VuexStore,
12
+ /**
13
+ * Unique ID for this request. Used for watch purposes
14
+ */
15
+ id: string,
16
+ /**
17
+ * Args used when determining if this resource type supports SSP
18
+ */
19
+ enabledFor: PaginationResourceContext,
20
+ /**
21
+ * Callback called when the resource is changed (notified by socket)
22
+ */
23
+ onChange?: () => void,
24
+
25
+ formatResponse?: {
26
+ /**
27
+ * Convert the response into a model class instance
28
+ */
29
+ classify?: boolean,
30
+ reactive?: boolean,
31
+ }
32
+ }
33
+
34
+ interface Result<T> extends Omit<ActionFindPageTransientResult<T>, 'data'> {
35
+ data: Reactive<T[]>
9
36
  }
10
37
 
11
38
  /**
12
- * This is a helper class that will assist in fetching a resource
13
- * - Handle if the resource can be fetched by the new pagination api
14
- * - Make a request to get a page (including classify)
15
- * - Provide updates when the resource changes
39
+ * This is a helper class that will assist in fetching a resource via the new Server-Side Pagination API
16
40
  *
17
41
  * This is designed to work in places where we don't/can't store the resource in the store
18
42
  * - There already exists a resource we don't want to overwrite
19
- * - We're transient and want something nicer than just cluster/request
43
+ * - We're transient and want something nicer than just `cluster/request` + all the trimmings
44
+ *
45
+ * It ...
46
+ * - Handles if the resource can be fetched by the new pagination api
47
+ * - Makes a request to get a page (including optional classify & reactive)
48
+ * - Provide updates when the resource changes
20
49
  */
21
- class PaginationWrapper<T = any> {
22
- private $store: VuexStore;
23
- private enabledFor: PaginationResourceContext;
24
-
25
- // Blocked on https://github.com/rancher/rancher/issues/40773 / https://github.com/rancher/dashboard/issues/12734
26
- private onUpdate: (out: Result<T>) => void;
27
-
28
- public isEnabled: boolean;
29
-
30
- constructor({
31
- $store,
32
- enabledFor,
33
- onUpdate,
34
- }: {
35
- $store: VuexStore,
36
- onUpdate: (res: Result<T>) => void,
37
- enabledFor: PaginationResourceContext,
38
- }) {
39
- this.$store = $store;
40
- this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters }, enabledFor);
41
- this.enabledFor = enabledFor;
42
- this.onUpdate = onUpdate;
50
+ class PaginationWrapper<T extends object> {
51
+ private $store: VuexStore;
52
+ private enabledFor: PaginationResourceContext;
53
+ private onChange?: STEVE_WATCH_EVENT_LISTENER_CALLBACK;
54
+ private id: string;
55
+ private classify: boolean;
56
+ private reactive: boolean;
57
+
58
+ public isEnabled: boolean;
59
+ private steveWatchParams: STEVE_WATCH_EVENT_PARAMS_COMMON | undefined;
60
+
61
+ constructor(args: Args) {
62
+ const {
63
+ $store, id, enabledFor, onChange, formatResponse
64
+ } = args;
65
+
66
+ this.$store = $store;
67
+ this.id = id;
68
+ this.enabledFor = enabledFor;
69
+ this.onChange = onChange;
70
+ this.classify = formatResponse?.classify || false;
71
+ this.reactive = formatResponse?.reactive || false;
72
+
73
+ this.isEnabled = paginationUtils.isEnabled({ rootGetters: $store.getters }, enabledFor);
74
+ }
75
+
76
+ async request(args: {
77
+ pagination: PaginationArgs,
78
+ }): Promise<Result<T>> {
79
+ if (!this.isEnabled) {
80
+ throw new Error(`Wrapper for type '${ this.enabledFor.store }/${ this.enabledFor.resource?.id }' in context '${ this.enabledFor.resource?.context }' not supported`);
81
+ }
82
+ const { pagination } = args;
83
+ const opt: ActionFindPageArgs = {
84
+ watch: false,
85
+ pagination,
86
+ transient: true,
87
+ };
88
+
89
+ // Fetch
90
+ const out: ActionFindPageTransientResult<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
91
+
92
+ // Watch
93
+ if (this.onChange && !this.steveWatchParams) {
94
+ this.steveWatchParams = {
95
+ event: STEVE_WATCH_EVENT.CHANGES,
96
+ id: this.id,
97
+ params: {
98
+ type: this.enabledFor.resource?.id as string,
99
+ mode: STEVE_WATCH_MODE.RESOURCE_CHANGES,
100
+ }
101
+ };
102
+
103
+ this.watch();
43
104
  }
44
105
 
45
- async request(args: {
46
- pagination: PaginationArgs,
47
- classify?: boolean,
48
- }): Promise<Result<T>> {
49
- if (!this.isEnabled) {
50
- throw new Error(`Wrapper for type '${ this.enabledFor.store }/${ this.enabledFor.resource?.id }' in context '${ this.enabledFor.resource?.context }' not supported`);
51
- }
52
- const { pagination, classify: doClassify } = args;
53
- const opt: ActionFindPageArgs = {
54
- transient: true,
55
- pagination
106
+ // Convert Response
107
+ if (this.classify) {
108
+ out.data = await this.$store.dispatch(`${ this.enabledFor.store }/createMany`, out.data);
109
+ }
110
+
111
+ if (this.reactive) {
112
+ return {
113
+ ...out,
114
+ data: reactive(out.data)
56
115
  };
116
+ }
57
117
 
58
- const out: Result<T> = await this.$store.dispatch(`${ this.enabledFor.store }/findPage`, { opt, type: this.enabledFor.resource?.id });
118
+ return out;
119
+ }
59
120
 
60
- if (doClassify) {
61
- for (let i = 0; i < out.data.length; i++) {
62
- out.data[i] = await this.$store.dispatch(`${ this.enabledFor.store }/create`, out.data[i]);
63
- }
64
- }
121
+ private async watch() {
122
+ if (!this.steveWatchParams) {
123
+ console.error('Calling watch but no watch params created'); // eslint-disable-line no-console
65
124
 
66
- return out;
125
+ return;
67
126
  }
127
+ const watchParams: STEVE_WATCH_EVENT_PARAMS = {
128
+ ...this.steveWatchParams,
129
+ callback: this.onChange as STEVE_WATCH_EVENT_LISTENER_CALLBACK, // we must have it by now
130
+ };
131
+
132
+ await this.$store.dispatch(`${ this.enabledFor.store }/watchEvent`, watchParams);
133
+ }
134
+
135
+ private async unWatch() {
136
+ if (!this.steveWatchParams) {
137
+ console.error('Calling unWatch but no watch params created'); // eslint-disable-line no-console
138
+
139
+ return;
140
+ }
141
+
142
+ const unWatchParams: STEVE_UNWATCH_EVENT_PARAMS = { ...this.steveWatchParams };
143
+
144
+ await this.$store.dispatch(`${ this.enabledFor.store }/unwatchEvent`, unWatchParams);
145
+ }
146
+
147
+ async onDestroy() {
148
+ await this.unWatch();
149
+ }
68
150
  }
69
151
 
70
152
  export default PaginationWrapper;
package/utils/settings.ts CHANGED
@@ -59,7 +59,10 @@ export const fetchInitialSettings = async(store: Store<any>): Promise<any> => {
59
59
  // We're authed, we will always get the full list
60
60
  return await store.dispatch('management/findAll', {
61
61
  type: MANAGEMENT.SETTING,
62
- opt: { url: `/v1/${ pluralize(MANAGEMENT.SETTING) }` }
62
+ opt: {
63
+ url: `/v1/${ pluralize(MANAGEMENT.SETTING) }`,
64
+ watch: false, // Watch requires FF and Settings, see `loadManagement` to see how this is handled
65
+ }
63
66
  } );
64
67
  }
65
68
 
package/utils/style.ts ADDED
@@ -0,0 +1,39 @@
1
+ export type StateColor = 'success' | 'warning' | 'error' | 'info' | 'disabled';
2
+ export const ALL_STATE_COLORS: StateColor[] = ['success', 'warning', 'error', 'info', 'disabled'];
3
+
4
+ export function stateColorCssVar(color: StateColor) {
5
+ return `var(--${ color })`;
6
+ }
7
+
8
+ export function toBgColor(color?: StateColor) {
9
+ const withDefaultColor = color || 'info';
10
+
11
+ return `bg-${ withDefaultColor }`;
12
+ }
13
+
14
+ /**
15
+ * Checks if 'a' is considered a higher alert than 'b'
16
+ * @param a target
17
+ * @param b comparison
18
+ * @returns true if 'a' is a higher alert than 'b' and false otherwise.
19
+ */
20
+ export function isHigherAlert(a: StateColor, b: StateColor) {
21
+ const order: StateColor[] = ['info', 'success', 'warning', 'error'];
22
+
23
+ const aIndex = order.indexOf(a);
24
+ const bIndex = order.indexOf(b);
25
+
26
+ return aIndex > bIndex;
27
+ }
28
+
29
+ export function getHighestAlertColor(colors: StateColor[]) {
30
+ let highestAlert: StateColor = 'info';
31
+
32
+ for (const color of colors) {
33
+ if (isHigherAlert(color, highestAlert)) {
34
+ highestAlert = color;
35
+ }
36
+ }
37
+
38
+ return highestAlert;
39
+ }