@rancher/shell 3.0.12-rc.2 → 3.0.12-rc.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.
- package/apis/impl/apis.ts +6 -0
- package/apis/index.ts +26 -0
- package/apis/intf/resources-api/cluster-api.ts +18 -0
- package/apis/intf/resources-api/mgmt-api.ts +15 -0
- package/apis/intf/resources-api/resource-base.ts +107 -0
- package/apis/intf/resources-api/resource-constants.ts +147 -0
- package/apis/intf/resources-api/resources-api.ts +143 -0
- package/apis/intf/resources.ts +49 -0
- package/apis/intf/{modal.ts → shell-api/modal.ts} +21 -26
- package/apis/intf/shell-api/proxy.ts +216 -0
- package/apis/intf/{slide-in.ts → shell-api/slide-in.ts} +4 -3
- package/apis/intf/{system.ts → shell-api/system.ts} +4 -1
- package/apis/intf/shell.ts +12 -6
- package/apis/resources/__tests__/resources-api-class.test.ts +550 -0
- package/apis/resources/index.ts +22 -0
- package/apis/resources/resources-api-class.ts +187 -0
- package/apis/shell/__tests__/proxy.test.ts +369 -0
- package/apis/shell/index.ts +8 -1
- package/apis/shell/modal.ts +4 -1
- package/apis/shell/notifications.ts +9 -6
- package/apis/shell/proxy.ts +256 -0
- package/apis/shell/slide-in.ts +4 -1
- package/apis/vue-shim.d.ts +2 -1
- package/assets/data/aws-regions.json +4 -0
- package/assets/fonts/lato/LatoLatin-Black.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Black.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BlackItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Bold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-BoldItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Heavy.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-HeavyItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Italic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Light.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-LightItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Medium.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-MediumItalic.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Regular.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff +0 -0
- package/assets/fonts/lato/LatoLatin-Semibold.woff2 +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff +0 -0
- package/assets/fonts/lato/LatoLatin-SemiboldItalic.woff2 +0 -0
- package/assets/styles/base/_variables.scss +2 -0
- package/assets/styles/fonts/_fontstack.scss +132 -8
- package/assets/translations/en-us.yaml +22 -5
- package/chart/monitoring/index.vue +10 -1
- package/components/ActionDropdownShell.vue +2 -1
- package/components/CruResourceFooter.vue +9 -5
- package/components/ExplorerProjectsNamespaces.vue +1 -1
- package/components/InstallHelmCharts.vue +2 -2
- package/components/LandingPagePreference.vue +14 -5
- package/components/Resource/Detail/Metadata/IdentifyingInformation/index.vue +15 -1
- package/components/Resource/Detail/Metadata/index.vue +6 -0
- package/components/Resource/Detail/ResourcePopover/index.vue +12 -1
- package/components/Resource/Detail/SpacedRow.vue +3 -1
- package/components/Resource/Detail/TitleBar/index.vue +10 -11
- package/components/ResourceList/Masthead.vue +12 -8
- package/components/SelectIconGrid.vue +0 -10
- package/components/SingleClusterInfo.vue +1 -0
- package/components/SortableTable/__tests__/sorting.test.ts +126 -0
- package/components/SortableTable/index.vue +6 -9
- package/components/SortableTable/selection.js +23 -5
- package/components/SortableTable/sorting.js +6 -3
- package/components/Wizard.vue +14 -13
- package/components/fleet/FleetBundles.vue +100 -12
- package/components/fleet/FleetClusterTargets/index.vue +37 -15
- package/components/fleet/__tests__/FleetClusterTargets.test.ts +149 -115
- package/components/fleet/__tests__/FleetClusters.test.ts +12 -12
- package/components/form/LabeledSelect.vue +20 -3
- package/components/form/NameNsDescription.vue +11 -0
- package/components/form/Security.vue +6 -2
- package/components/form/WorkloadPorts.vue +2 -7
- package/components/form/__tests__/Security.test.ts +76 -0
- package/components/formatter/Autoscaler.vue +4 -4
- package/components/formatter/ClusterKubeVersion.vue +27 -0
- package/components/formatter/ClusterLink.vue +1 -7
- package/components/formatter/ClusterProvider.vue +6 -10
- package/components/formatter/FleetSummaryGraph.vue +0 -3
- package/components/formatter/MachineSummaryGraph.vue +1 -1
- package/components/formatter/PodsUsage.vue +2 -2
- package/components/formatter/__tests__/Autoscaler.test.ts +19 -22
- package/components/formatter/__tests__/FleetSummaryGraph.test.ts +216 -0
- package/components/formatter/__tests__/PodsUsage.test.ts +6 -10
- package/components/nav/NamespaceFilter.vue +2 -2
- package/components/nav/TopLevelMenu.helper.ts +15 -3
- package/components/nav/TopLevelMenu.vue +16 -5
- package/components/nav/__tests__/TopLevelMenu.test.ts +145 -21
- package/components/templates/home.vue +18 -0
- package/components/templates/plain.vue +18 -0
- package/components/templates/standalone.vue +17 -0
- package/composables/useFormValidation.ts +93 -0
- package/composables/useVeeValidateField.test.ts +159 -0
- package/composables/useVeeValidateField.ts +67 -0
- package/config/pagination-table-headers.js +18 -1
- package/config/product/manager.js +82 -21
- package/config/router/routes.js +6 -0
- package/config/table-headers.js +20 -1
- package/config/types.js +2 -1
- package/core/__tests__/plugin-products.test.ts +904 -20
- package/core/plugin-products-base.ts +107 -7
- package/core/plugin-products.ts +4 -0
- package/core/plugin-types.ts +111 -1
- package/core/plugin.ts +15 -7
- package/core/productDebugger.js +9 -4
- package/core/types-provisioning.ts +43 -30
- package/core/types.ts +57 -20
- package/detail/__tests__/pod.test.ts +41 -0
- package/detail/harvesterhci.io.management.cluster.vue +6 -2
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +4 -10
- package/edit/auth/__tests__/azuread.test.ts +217 -34
- package/edit/auth/azuread.vue +122 -14
- package/edit/auth/oidc.vue +2 -2
- package/edit/networking.k8s.io.ingress/DefaultBackend.vue +13 -4
- package/edit/networking.k8s.io.ingress/RulePath.vue +8 -4
- package/edit/networking.k8s.io.ingress/index.vue +75 -20
- package/edit/provisioning.cattle.io.cluster/__tests__/MachinePool.test.ts +104 -0
- package/edit/provisioning.cattle.io.cluster/index.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/rke2.vue +8 -4
- package/edit/provisioning.cattle.io.cluster/tabs/MachinePool.vue +11 -0
- package/edit/provisioning.cattle.io.cluster/tabs/registries/RegistryConfigs.vue +37 -4
- package/edit/provisioning.cattle.io.cluster/tabs/registries/__tests__/RegistryConfigs.test.ts +132 -7
- package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +2 -1
- package/edit/secret/__tests__/ssh.test.ts +5 -6
- package/edit/secret/basic.vue +31 -0
- package/edit/secret/index.vue +68 -17
- package/edit/secret/registry.vue +38 -0
- package/edit/secret/ssh.vue +29 -0
- package/edit/secret/tls.vue +30 -0
- package/edit/service.vue +4 -4
- package/edit/workload/Upgrading.vue +3 -3
- package/edit/workload/__tests__/Upgrading.test.ts +6 -9
- package/edit/workload/mixins/workload.js +2 -1
- package/list/fleet.cattle.io.bundle.vue +7 -104
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +20 -0
- package/list/provisioning.cattle.io.cluster.vue +262 -180
- package/list/utils/management.cattle.io.cluster.utils.ts +128 -0
- package/mixins/__tests__/chart.test.ts +112 -0
- package/mixins/brand.js +2 -1
- package/mixins/chart.js +12 -8
- package/mixins/resource-fetch-api-pagination.js +41 -5
- package/models/__tests__/ext.cattle.io.kubeconfig.test.ts +67 -67
- package/models/__tests__/management.cattle.io.cluster.test.ts +1 -1
- package/models/__tests__/management.cattle.io.node.ts +6 -5
- package/models/__tests__/management.cattle.io.nodepool.ts +5 -4
- package/models/__tests__/provisioning.cattle.io.cluster.test.ts +32 -11
- package/models/base-cluster.x-k8s.io.js +26 -0
- package/models/cluster.js +1 -1
- package/models/cluster.x-k8s.io.machine.js +4 -22
- package/models/cluster.x-k8s.io.machinedeployment.js +2 -20
- package/models/cluster.x-k8s.io.machineset.js +2 -20
- package/models/compliance.cattle.io.clusterscan.js +130 -2
- package/models/ext.cattle.io.kubeconfig.ts +4 -7
- package/models/fleet-application.js +3 -1
- package/models/management.cattle.io.cluster.js +417 -40
- package/models/management.cattle.io.node.js +6 -4
- package/models/management.cattle.io.nodepool.js +1 -1
- package/models/networking.k8s.io.ingress.js +12 -4
- package/models/provisioning.cattle.io.cluster.js +47 -330
- package/models/rke.cattle.io.etcdsnapshot.js +1 -2
- package/package.json +11 -29
- package/pages/__tests__/readme.test.ts +49 -0
- package/pages/auth/setup.vue +2 -3
- package/pages/c/_cluster/apps/charts/__tests__/chart.test.ts +76 -0
- package/pages/c/_cluster/apps/charts/chart.vue +60 -8
- package/pages/c/_cluster/apps/charts/install.vue +10 -7
- package/pages/c/_cluster/explorer/__tests__/index.test.ts +23 -25
- package/pages/c/_cluster/explorer/index.vue +5 -49
- package/pages/c/_cluster/istio/__tests__/istio.index.test.ts +194 -0
- package/pages/c/_cluster/istio/index.vue +21 -6
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +1 -0
- package/pages/c/_cluster/uiplugins/__tests__/index.test.ts +719 -2
- package/pages/c/_cluster/uiplugins/index.vue +203 -197
- package/pages/diagnostic.vue +13 -17
- package/pages/fail-whale.vue +18 -0
- package/pages/home.vue +77 -260
- package/pages/readme.vue +88 -0
- package/plugins/dashboard-store/__tests__/resource-class.test.ts +88 -0
- package/plugins/dashboard-store/actions.js +40 -18
- package/plugins/dashboard-store/resource-class.js +5 -2
- package/plugins/steve/__tests__/subscribe.spec.ts +6 -3
- package/plugins/steve/steve-pagination-utils.ts +11 -3
- package/plugins/steve/subscribe.js +35 -5
- package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +10 -4
- package/rancher-components/Form/LabeledInput/LabeledInput.vue +7 -52
- package/rancher-components/RcButton/RcButton.test.ts +37 -1
- package/rancher-components/RcButton/RcButton.vue +38 -8
- package/rancher-components/RcDropdown/RcDropdownTrigger.vue +10 -8
- package/store/__tests__/catalog.test.ts +115 -1
- package/store/__tests__/type-map.test.ts +556 -1
- package/store/action-menu.js +8 -3
- package/store/auth.js +1 -1
- package/store/aws.js +27 -16
- package/store/catalog.js +27 -3
- package/store/digitalocean.js +20 -38
- package/store/index.js +2 -0
- package/store/linode.js +25 -40
- package/store/pnap.js +1 -0
- package/store/type-map.js +111 -29
- package/tsconfig.paths.json +8 -8
- package/types/kube/kube-api.ts +14 -1
- package/types/rancher/steve.api.ts +12 -12
- package/types/resources/settings.d.ts +2 -1
- package/types/shell/index.d.ts +102 -2
- package/types/store/dashboard-store.types.ts +108 -11
- package/types/store/pagination.types.ts +6 -3
- package/utils/__tests__/alertmanagerconfig.test.ts +117 -0
- package/utils/__tests__/async.test.ts +87 -0
- package/utils/__tests__/aws.test.ts +140 -0
- package/utils/__tests__/banners.test.ts +176 -0
- package/utils/__tests__/chart.test.ts +64 -1
- package/utils/__tests__/color.test.ts +226 -0
- package/utils/__tests__/duration.test.ts +140 -0
- package/utils/__tests__/fleet.test.ts +340 -0
- package/utils/__tests__/ingress.test.ts +553 -0
- package/utils/__tests__/kube.test.ts +68 -0
- package/utils/__tests__/namespace-filter.test.ts +109 -0
- package/utils/__tests__/pagination-utils.test.ts +361 -0
- package/utils/__tests__/parse-externalid.test.ts +137 -0
- package/utils/__tests__/perf-setting.utils.test.ts +98 -0
- package/utils/__tests__/poller-sequential.test.ts +177 -0
- package/utils/__tests__/poller.test.ts +170 -0
- package/utils/__tests__/promise.test.ts +346 -0
- package/utils/__tests__/settings.test.ts +140 -0
- package/utils/__tests__/sort-utils.test.ts +301 -0
- package/utils/__tests__/string-utils.test.ts +798 -0
- package/utils/__tests__/string.test.ts +23 -1
- package/utils/__tests__/style.test.ts +154 -0
- package/utils/__tests__/svg-filter.test.ts +184 -0
- package/utils/__tests__/units.test.ts +417 -0
- package/utils/__tests__/versions.test.ts +128 -0
- package/utils/__tests__/xccdf.test.ts +391 -0
- package/utils/chart.js +36 -0
- package/utils/fleet.ts +13 -3
- package/utils/gatekeeper/__tests__/util.test.ts +174 -0
- package/utils/gc/__tests__/gc-interval.test.ts +119 -0
- package/utils/gc/__tests__/gc-root-store.test.ts +225 -0
- package/utils/gc/__tests__/gc-route-changed.test.ts +96 -0
- package/utils/gc/__tests__/gc.test.ts +487 -0
- package/utils/ingress.ts +9 -1
- package/utils/pagination-utils.ts +2 -1
- package/utils/string.js +25 -2
- package/utils/uiplugins.ts +5 -5
- package/utils/validators/__tests__/cluster-name.test.ts +110 -0
- package/utils/validators/__tests__/cron-schedule.test.ts +79 -0
- package/utils/validators/__tests__/index.test.ts +481 -0
- package/utils/validators/__tests__/kubernetes-name.test.ts +163 -0
- package/utils/validators/__tests__/misc-validators.test.ts +246 -0
- package/utils/validators/__tests__/pod-affinity.test.ts +382 -0
- package/utils/validators/__tests__/prometheusrule.test.ts +211 -0
- package/utils/validators/__tests__/role-template.test.ts +149 -0
- package/utils/validators/__tests__/service.test.ts +283 -0
- package/utils/validators/__tests__/setting.test.js +32 -0
- package/utils/validators/formRules/__tests__/index.test.ts +50 -0
- package/utils/validators/formRules/index.ts +5 -5
- package/utils/validators/machine-pool.ts +1 -1
- package/utils/validators/setting.js +18 -3
- package/utils/xccdf.ts +418 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-700.woff2 +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff +0 -0
- package/assets/fonts/lato/lato-v17-latin-regular.woff2 +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ResourceType, FindMethodOptions, FindAllMethodOptions, FindFilteredPageOptions, FindFilteredLabelSelectorOptions,
|
|
3
|
+
FindFilteredPageResponse, FindFilteredLabelSelectorResponse
|
|
4
|
+
} from '@shell/apis/intf/resources-api/resource-base';
|
|
5
|
+
import { ResourcesApi } from '@shell/apis/intf/resources-api/resources-api';
|
|
6
|
+
import { SteveListResponse, SteveGetResponse } from '@shell/types/rancher/steve.api';
|
|
7
|
+
import { Store } from 'vuex';
|
|
8
|
+
|
|
9
|
+
export class ResourcesApiClassImpl implements ResourcesApi {
|
|
10
|
+
private store: Store<any>;
|
|
11
|
+
|
|
12
|
+
private storeType: 'cluster' | 'management' | string;
|
|
13
|
+
|
|
14
|
+
private surfaceError(message: string, e?: any): never {
|
|
15
|
+
console.error(`Resource API error - ${ this.storeType } - ${ message }`); // eslint-disable-line no-console
|
|
16
|
+
throw new Error(`Resource API error - ${ this.storeType } - ${ message }`, { cause: e });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private isNamespaced(resourceType: ResourceType): boolean {
|
|
20
|
+
const schema = this.store.getters[`${ this.storeType }/schemaFor`]?.(resourceType);
|
|
21
|
+
|
|
22
|
+
return !!schema?.attributes?.namespaced;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
constructor(store: Store<any>, storeType: 'cluster' | 'management' | string) {
|
|
26
|
+
this.store = store;
|
|
27
|
+
this.storeType = storeType;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Finds a specific resource by its type and ID.
|
|
32
|
+
*
|
|
33
|
+
* @template T - The type of the resource (defaults to SteveGetResponse)
|
|
34
|
+
* @param resourceType - The type of the resource to find (use **{@link K8S}** constant). See also {@link ResourceType}.
|
|
35
|
+
* @param resourceId - The unique identifier of the resource to find. If the resource is namespaced, this should be in the format `namespace/name`.
|
|
36
|
+
* @param options - Optional find arguments
|
|
37
|
+
* @returns The found resource item or null if not found.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { useResources, K8S } from '@shell/apis';
|
|
42
|
+
*
|
|
43
|
+
* const resources = useResources();
|
|
44
|
+
*
|
|
45
|
+
* // Namespaced resource - ID must be in "namespace/name" format
|
|
46
|
+
* const pod = await resources.cluster.find(K8S.POD, 'default/my-pod-123');
|
|
47
|
+
*
|
|
48
|
+
* // Cluster-scoped resource - ID is just the name
|
|
49
|
+
* const node = await resources.cluster.find(K8S.NODE, 'worker-1');
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
async find<T = SteveGetResponse>(
|
|
53
|
+
resourceType: ResourceType,
|
|
54
|
+
resourceId: string,
|
|
55
|
+
options?: FindMethodOptions
|
|
56
|
+
): Promise<T | null> {
|
|
57
|
+
if (this.isNamespaced(resourceType) && !resourceId.includes('/')) {
|
|
58
|
+
this.surfaceError(`Resource "${ resourceType }" is namespaced. The resourceId must be in "namespace/name" format, but received "${ resourceId }"`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const resource = await this.store.dispatch(`${ this.storeType }/find`, {
|
|
63
|
+
type: resourceType,
|
|
64
|
+
id: resourceId,
|
|
65
|
+
opt: options || {}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return (resource as T) ?? null;
|
|
69
|
+
} catch (e: unknown) {
|
|
70
|
+
this.surfaceError(`Failed to find resource ${ resourceType }/${ resourceId }: ${ (e as Error).message }`, e);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Finds resources using pagination mode with server-side filtering, sorting, and pagination.
|
|
76
|
+
*
|
|
77
|
+
* Requires `ui-sql-cache` to be enabled.
|
|
78
|
+
*
|
|
79
|
+
* @template T - The type of the resources (defaults to SteveListResponse)
|
|
80
|
+
* @param resourceType - The type of the resources to find
|
|
81
|
+
* @param options - Pagination options with server-side filtering and sorting
|
|
82
|
+
* @returns Response containing resource items (may be transient if requested, otherwise cached array)
|
|
83
|
+
* @throws If pagination mode is requested but `ui-sql-cache` is not enabled
|
|
84
|
+
*/
|
|
85
|
+
findFiltered<T = SteveListResponse>(
|
|
86
|
+
resourceType: ResourceType,
|
|
87
|
+
options: FindFilteredPageOptions
|
|
88
|
+
): Promise<FindFilteredPageResponse<T>>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Finds resources using label selector matching.
|
|
92
|
+
*
|
|
93
|
+
* Filters resources by Kubernetes labels. The store automatically handles pagination:
|
|
94
|
+
* - If `ui-sql-cache` is enabled: uses server-side pagination
|
|
95
|
+
* - Otherwise: uses native Kubernetes API pagination
|
|
96
|
+
*
|
|
97
|
+
* @template T - The type of the resources (defaults to SteveListResponse)
|
|
98
|
+
* @param resourceType - The type of the resources to find
|
|
99
|
+
* @param options - Label selector options for filtering
|
|
100
|
+
* @returns Response containing resource items (may be transient if requested, otherwise cached array)
|
|
101
|
+
*/
|
|
102
|
+
findFiltered<T = SteveListResponse>(
|
|
103
|
+
resourceType: ResourceType,
|
|
104
|
+
options: FindFilteredLabelSelectorOptions
|
|
105
|
+
): Promise<FindFilteredLabelSelectorResponse<T>>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @internal Implementation - use one of the overloads above
|
|
109
|
+
*/
|
|
110
|
+
async findFiltered<T = SteveListResponse>(
|
|
111
|
+
resourceType: ResourceType,
|
|
112
|
+
options: FindFilteredPageOptions | FindFilteredLabelSelectorOptions
|
|
113
|
+
): Promise<FindFilteredPageResponse<T> | FindFilteredLabelSelectorResponse<T>> {
|
|
114
|
+
try {
|
|
115
|
+
if ('pagination' in options) { // pagination mode
|
|
116
|
+
const canPaginate = this.store.getters[`${ this.storeType }/paginationEnabled`]?.(resourceType);
|
|
117
|
+
|
|
118
|
+
if (!canPaginate) {
|
|
119
|
+
return this.surfaceError('findFiltered requests with FindFilteredPageOptions are only supported when ui-sql-cache is enabled');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const safeOption = options as FindFilteredPageOptions;
|
|
123
|
+
const response = await this.store.dispatch(`${ this.storeType }/findPage`, {
|
|
124
|
+
type: resourceType,
|
|
125
|
+
opt: safeOption
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return response as FindFilteredPageResponse<T>;
|
|
129
|
+
} else if ('labelSelector' in options) { // label selector mode
|
|
130
|
+
const safeOption = options as FindFilteredLabelSelectorOptions;
|
|
131
|
+
const { labelSelector, namespaced, ...rest } = safeOption;
|
|
132
|
+
const resources = await this.store.dispatch(`${ this.storeType }/findLabelSelector`, {
|
|
133
|
+
type: resourceType,
|
|
134
|
+
matching: {
|
|
135
|
+
namespace: namespaced,
|
|
136
|
+
labelSelector
|
|
137
|
+
},
|
|
138
|
+
opt: rest
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return resources as FindFilteredLabelSelectorResponse<T>;
|
|
142
|
+
} else {
|
|
143
|
+
return this.surfaceError('findFiltered request was made with unknown options');
|
|
144
|
+
}
|
|
145
|
+
} catch (e: unknown) {
|
|
146
|
+
this.surfaceError(`Failed to find filtered resources ${ resourceType }: ${ (e as Error).message }`, e);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Fetches all resources of a specific type with advanced options.
|
|
152
|
+
* This method provides additional capabilities like incremental loading and namespace filtering.
|
|
153
|
+
*
|
|
154
|
+
* @template T - The type of the resources (defaults to SteveListResponse)
|
|
155
|
+
* @param resourceType - The type of the resources to find (use **{@link K8S}** constant). See also {@link ResourceType}.
|
|
156
|
+
* @param options - Optional advanced fetch options (incremental loading, namespace filtering, etc.)
|
|
157
|
+
* @returns An array of resource items or an empty array if none are found.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* import { useResources, K8S } from '@shell/apis';
|
|
162
|
+
* import type { Pod } from '@shell/types/resources';
|
|
163
|
+
*
|
|
164
|
+
* const resources = useResources();
|
|
165
|
+
*
|
|
166
|
+
* // Fetch all pods in specific namespaces
|
|
167
|
+
* const pods = await resources.cluster.findAll<Pod>(K8S.POD, {
|
|
168
|
+
* namespaced: ['default', 'kube-system']
|
|
169
|
+
* });
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
async findAll<T = SteveListResponse>(
|
|
173
|
+
resourceType: ResourceType,
|
|
174
|
+
options?: FindAllMethodOptions
|
|
175
|
+
): Promise<T[]> {
|
|
176
|
+
try {
|
|
177
|
+
const resources = await this.store.dispatch(`${ this.storeType }/findAll`, {
|
|
178
|
+
type: resourceType,
|
|
179
|
+
opt: options || {}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return resources as T[];
|
|
183
|
+
} catch (e: unknown) {
|
|
184
|
+
this.surfaceError(`Failed to find all resources ${ resourceType }: ${ (e as Error).message }`, e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
// proxy.test.ts
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, it, expect, jest, beforeEach
|
|
5
|
+
} from '@jest/globals';
|
|
6
|
+
import { ProxyApiImpl, createDepaginator } from '../proxy';
|
|
7
|
+
import { Store } from 'vuex';
|
|
8
|
+
|
|
9
|
+
const PROXY_PREFIX = '/meta/proxy/';
|
|
10
|
+
|
|
11
|
+
describe('proxyApiImpl', () => {
|
|
12
|
+
let mockStore: Store<any>;
|
|
13
|
+
let mockDispatch: jest.Mock;
|
|
14
|
+
let proxyApi: ProxyApiImpl;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockDispatch = jest.fn() as any;
|
|
18
|
+
mockStore = { dispatch: mockDispatch } as any;
|
|
19
|
+
proxyApi = new ProxyApiImpl(mockStore);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// prepareRequest — URL building
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
describe('prepareRequest: URL building', () => {
|
|
27
|
+
it('should prepend /meta/proxy/ to a url, stripping the https scheme', () => {
|
|
28
|
+
const { url } = proxyApi.prepareRequest({ url: new URL('https://api.example.com/v1/things') });
|
|
29
|
+
|
|
30
|
+
expect(url).toBe(`${ PROXY_PREFIX }api.example.com/v1/things`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should preserve http:/ (one slash) for plain-HTTP urls', () => {
|
|
34
|
+
const { url } = proxyApi.prepareRequest({ url: new URL('http://api.example.com:1234/v1/things') });
|
|
35
|
+
|
|
36
|
+
expect(url).toBe(`${ PROXY_PREFIX }http:/api.example.com:1234/v1/things`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should preserve query params', () => {
|
|
40
|
+
const input = new URL('https://api.example.com/v1/things');
|
|
41
|
+
|
|
42
|
+
input.searchParams.set('region', 'us-east-1');
|
|
43
|
+
input.searchParams.set('page', '2');
|
|
44
|
+
|
|
45
|
+
const { url } = proxyApi.prepareRequest({ url: input });
|
|
46
|
+
|
|
47
|
+
expect(url).toBe(`${ PROXY_PREFIX }api.example.com/v1/things?region=us-east-1&page=2`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// prepareRequest — header building
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
describe('prepareRequest: header building', () => {
|
|
56
|
+
it('should set Accept to application/json by default', () => {
|
|
57
|
+
const { headers } = proxyApi.prepareRequest({ url: new URL('https://api.example.com') });
|
|
58
|
+
|
|
59
|
+
expect(headers['Accept']).toBe('application/json');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should override Accept when accept option is provided', () => {
|
|
63
|
+
const { headers } = proxyApi.prepareRequest({ url: new URL('https://api.example.com'), accept: 'text/plain' });
|
|
64
|
+
|
|
65
|
+
expect(headers['Accept']).toBe('text/plain');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should merge caller-supplied headers', () => {
|
|
69
|
+
const { headers } = proxyApi.prepareRequest({
|
|
70
|
+
url: new URL('https://api.example.com'),
|
|
71
|
+
headers: { 'X-Custom': 'value' },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(headers['X-Custom']).toBe('value');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should set x-api-auth-header with Bearer scheme for a token by default', () => {
|
|
78
|
+
const { headers } = proxyApi.prepareRequest({
|
|
79
|
+
url: new URL('https://api.example.com'),
|
|
80
|
+
authentication: { token: 'my-token' },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(headers['x-api-auth-header']).toBe('Bearer my-token');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should use the supplied authSigner as the scheme for a token', () => {
|
|
87
|
+
const { headers } = proxyApi.prepareRequest({
|
|
88
|
+
url: new URL('https://api.example.com'),
|
|
89
|
+
authentication: { token: 'encoded==', authSigner: 'basic' },
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(headers['x-api-auth-header']).toBe('basic encoded==');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should set x-api-cattleauth-header with signer and credID', () => {
|
|
96
|
+
const { headers } = proxyApi.prepareRequest({
|
|
97
|
+
url: new URL('https://api.example.com'),
|
|
98
|
+
authentication: {
|
|
99
|
+
id: 'cattle-global-data:my-cred',
|
|
100
|
+
authSigner: 'bearer',
|
|
101
|
+
passwordField: 'token',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(headers['x-api-cattleauth-header']).toContain('bearer ');
|
|
106
|
+
expect(headers['x-api-cattleauth-header']).toContain('credID=cattle-global-data:my-cred');
|
|
107
|
+
expect(headers['x-api-cattleauth-header']).toContain('passwordField=token');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should include usernameField in x-api-cattleauth-header when provided', () => {
|
|
111
|
+
const { headers } = proxyApi.prepareRequest({
|
|
112
|
+
url: new URL('https://api.example.com'),
|
|
113
|
+
authentication: {
|
|
114
|
+
id: 'cattle-global-data:my-cred',
|
|
115
|
+
authSigner: 'basic',
|
|
116
|
+
usernameField: 'username',
|
|
117
|
+
passwordField: 'password',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(headers['x-api-cattleauth-header']).toContain('usernameField=username');
|
|
122
|
+
expect(headers['x-api-cattleauth-header']).toContain('passwordField=password');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should not set x-api-cattleauth-header when no authentication is provided', () => {
|
|
126
|
+
const { headers } = proxyApi.prepareRequest({ url: new URL('https://api.example.com') });
|
|
127
|
+
|
|
128
|
+
expect(headers['x-api-cattleauth-header']).toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should not set x-api-auth-header when no authentication is provided', () => {
|
|
132
|
+
const { headers } = proxyApi.prepareRequest({ url: new URL('https://api.example.com') });
|
|
133
|
+
|
|
134
|
+
expect(headers['x-api-auth-header']).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// request
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
describe('request', () => {
|
|
143
|
+
it('should dispatch management/request with the built url and headers', async() => {
|
|
144
|
+
mockDispatch.mockResolvedValue({ data: [] });
|
|
145
|
+
|
|
146
|
+
await proxyApi.request({ url: new URL('https://api.example.com/v1/items') });
|
|
147
|
+
|
|
148
|
+
expect(mockDispatch).toHaveBeenCalledWith(
|
|
149
|
+
'management/request',
|
|
150
|
+
expect.objectContaining({
|
|
151
|
+
url: `${ PROXY_PREFIX }api.example.com/v1/items`,
|
|
152
|
+
redirectUnauthorized: false,
|
|
153
|
+
}),
|
|
154
|
+
{ root: true }
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should pass method through to management/request', async() => {
|
|
159
|
+
mockDispatch.mockResolvedValue({});
|
|
160
|
+
|
|
161
|
+
await proxyApi.request({ url: new URL('https://api.example.com/token'), method: 'POST' });
|
|
162
|
+
|
|
163
|
+
expect(mockDispatch).toHaveBeenCalledWith(
|
|
164
|
+
'management/request',
|
|
165
|
+
expect.objectContaining({ method: 'POST' }),
|
|
166
|
+
{ root: true }
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should return the response directly when no postProcess is set', async() => {
|
|
171
|
+
const mockResponse = { items: ['a', 'b'] };
|
|
172
|
+
|
|
173
|
+
mockDispatch.mockResolvedValue(mockResponse);
|
|
174
|
+
|
|
175
|
+
const result = await proxyApi.request({ url: new URL('https://api.example.com/items') });
|
|
176
|
+
|
|
177
|
+
expect(result).toStrictEqual(mockResponse);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should call postProcess with the response and return its result', async() => {
|
|
181
|
+
const raw = { items: ['a'] };
|
|
182
|
+
const processed = { items: ['a', 'b'] };
|
|
183
|
+
|
|
184
|
+
mockDispatch.mockResolvedValue(raw);
|
|
185
|
+
const postProcess = jest.fn().mockResolvedValue(processed) as jest.Mock;
|
|
186
|
+
|
|
187
|
+
const result = await proxyApi.request({ url: new URL('https://api.example.com/items'), postProcess });
|
|
188
|
+
|
|
189
|
+
expect(postProcess).toHaveBeenCalledWith(raw);
|
|
190
|
+
expect(result).toStrictEqual(processed);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('createDepaginator: should follow next links and merge results', async() => {
|
|
194
|
+
const page1 = {
|
|
195
|
+
items: ['a', 'b'],
|
|
196
|
+
links: { pages: { next: 'https://api.example.com/v1/items?page=2' } },
|
|
197
|
+
};
|
|
198
|
+
const page2 = {
|
|
199
|
+
items: ['c'],
|
|
200
|
+
links: { pages: { next: null } },
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
mockDispatch
|
|
204
|
+
.mockResolvedValueOnce(page1)
|
|
205
|
+
.mockResolvedValueOnce(page2);
|
|
206
|
+
|
|
207
|
+
const baseOptions = { url: new URL('https://api.example.com/v1/items') };
|
|
208
|
+
const result = await proxyApi.request({
|
|
209
|
+
...baseOptions,
|
|
210
|
+
postProcess: createDepaginator(proxyApi, baseOptions, { mergeKey: 'items' }),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(result.items).toStrictEqual(['a', 'b', 'c']);
|
|
214
|
+
expect(mockDispatch).toHaveBeenCalledTimes(2);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('createDepaginator: should use a custom nextUrlPath when provided', async() => {
|
|
218
|
+
const page1 = {
|
|
219
|
+
data: ['x'],
|
|
220
|
+
paging: { next: 'https://api.example.com/v1/things?cursor=abc' },
|
|
221
|
+
};
|
|
222
|
+
const page2 = {
|
|
223
|
+
data: ['y'],
|
|
224
|
+
paging: { next: null },
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
mockDispatch
|
|
228
|
+
.mockResolvedValueOnce(page1)
|
|
229
|
+
.mockResolvedValueOnce(page2);
|
|
230
|
+
|
|
231
|
+
const baseOptions = { url: new URL('https://api.example.com/v1/things') };
|
|
232
|
+
const result = await proxyApi.request({
|
|
233
|
+
...baseOptions,
|
|
234
|
+
postProcess: createDepaginator(proxyApi, baseOptions, {
|
|
235
|
+
nextUrlPath: 'paging.next',
|
|
236
|
+
mergeKey: 'data',
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(result.data).toStrictEqual(['x', 'y']);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// allowDomains
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
describe('allowDomains', () => {
|
|
249
|
+
it('should dispatch management/create with the correct type and routes', async() => {
|
|
250
|
+
const mockSave = jest.fn().mockResolvedValue({ spec: { routes: [] } });
|
|
251
|
+
|
|
252
|
+
mockDispatch.mockResolvedValue({ save: mockSave });
|
|
253
|
+
|
|
254
|
+
await proxyApi.allowDomains(['api.example.com', '%.amazonaws.com'], 'my-cr');
|
|
255
|
+
|
|
256
|
+
expect(mockDispatch).toHaveBeenCalledWith('management/create', {
|
|
257
|
+
type: 'management.cattle.io.proxyEndpoint',
|
|
258
|
+
metadata: { name: 'my-cr' },
|
|
259
|
+
spec: {
|
|
260
|
+
routes: [
|
|
261
|
+
{ domain: 'api.example.com' },
|
|
262
|
+
{ domain: '%.amazonaws.com' },
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(mockSave).toHaveBeenCalledTimes(1);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should use an generateName when none is supplied', async() => {
|
|
271
|
+
const mockSave = jest.fn().mockResolvedValue({});
|
|
272
|
+
|
|
273
|
+
mockDispatch.mockResolvedValue({ save: mockSave });
|
|
274
|
+
|
|
275
|
+
await proxyApi.allowDomains(['api.example.com']);
|
|
276
|
+
|
|
277
|
+
expect(mockDispatch).toHaveBeenCalledWith('management/create', expect.objectContaining({
|
|
278
|
+
metadata: { generateName: 'endpoints-' }, spec: { routes: [{ domain: 'api.example.com' }] }, type: 'management.cattle.io.proxyEndpoint'
|
|
279
|
+
}));
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// isDomainAllowed
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
describe('isDomainAllowed', () => {
|
|
288
|
+
it('should return true when the domain is found in a ProxyEndpoint CR', async() => {
|
|
289
|
+
mockDispatch.mockResolvedValue([
|
|
290
|
+
{ spec: { routes: [{ domain: 'api.example.com' }, { domain: 'other.example.com' }] } },
|
|
291
|
+
]);
|
|
292
|
+
|
|
293
|
+
const result = await proxyApi.isDomainAllowed('api.example.com');
|
|
294
|
+
|
|
295
|
+
expect(result).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should match case-insensitively', async() => {
|
|
299
|
+
mockDispatch.mockResolvedValue([
|
|
300
|
+
{ spec: { routes: [{ domain: 'API.EXAMPLE.COM' }] } },
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
const result = await proxyApi.isDomainAllowed('api.example.com');
|
|
304
|
+
|
|
305
|
+
expect(result).toBe(true);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should return false when the domain is not found in any CR', async() => {
|
|
309
|
+
mockDispatch.mockResolvedValue([
|
|
310
|
+
{ spec: { routes: [{ domain: 'other.example.com' }] } },
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
const result = await proxyApi.isDomainAllowed('api.example.com');
|
|
314
|
+
|
|
315
|
+
expect(result).toBe(false);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should return false when there are no ProxyEndpoint CRs', async() => {
|
|
319
|
+
mockDispatch.mockResolvedValue([]);
|
|
320
|
+
|
|
321
|
+
const result = await proxyApi.isDomainAllowed('api.example.com');
|
|
322
|
+
|
|
323
|
+
expect(result).toBe(false);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should return false when a CR has no routes', async() => {
|
|
327
|
+
mockDispatch.mockResolvedValue([{ spec: {} }]);
|
|
328
|
+
|
|
329
|
+
const result = await proxyApi.isDomainAllowed('api.example.com');
|
|
330
|
+
|
|
331
|
+
expect(result).toBe(false);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// hasProxyEndpoint
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
describe('hasProxyEndpoint', () => {
|
|
340
|
+
it('should return true when management/find resolves', async() => {
|
|
341
|
+
mockDispatch.mockResolvedValue({ metadata: { name: 'my-cr' } });
|
|
342
|
+
|
|
343
|
+
const result = await proxyApi.hasProxyEndpoint('my-cr');
|
|
344
|
+
|
|
345
|
+
expect(result).toBe(true);
|
|
346
|
+
expect(mockDispatch).toHaveBeenCalledWith('management/find', {
|
|
347
|
+
type: 'management.cattle.io.proxyEndpoint',
|
|
348
|
+
id: 'my-cr',
|
|
349
|
+
opt: { force: false },
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should return false when management/find rejects with status 404', async() => {
|
|
354
|
+
mockDispatch.mockRejectedValue({ status: 404 });
|
|
355
|
+
|
|
356
|
+
const result = await proxyApi.hasProxyEndpoint('missing-cr');
|
|
357
|
+
|
|
358
|
+
expect(result).toBe(false);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should re-throw errors that are not 404', async() => {
|
|
362
|
+
const serverError = { status: 500, message: 'Internal Server Error' };
|
|
363
|
+
|
|
364
|
+
mockDispatch.mockRejectedValue(serverError);
|
|
365
|
+
|
|
366
|
+
await expect(proxyApi.hasProxyEndpoint('my-cr')).rejects.toStrictEqual(serverError);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
});
|
package/apis/shell/index.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { Store } from 'vuex';
|
|
2
2
|
import {
|
|
3
|
-
ModalApi, ShellApi, SlideInApi, NotificationApi, SystemApi
|
|
3
|
+
ModalApi, ShellApi, SlideInApi, NotificationApi, SystemApi, ProxyApi
|
|
4
4
|
} from '@shell/apis/intf/shell';
|
|
5
5
|
import { ModalApiImpl } from './modal';
|
|
6
6
|
import { SlideInApiImpl } from './slide-in';
|
|
7
7
|
import { NotificationApiImpl } from './notifications';
|
|
8
8
|
import { SystemApiImpl } from './system';
|
|
9
|
+
import { ProxyApiImpl } from './proxy';
|
|
9
10
|
|
|
10
11
|
export class ShellApiImpl implements ShellApi {
|
|
11
12
|
private modalApi: ModalApi;
|
|
12
13
|
private slideInApi: SlideInApi;
|
|
13
14
|
private notificationApi: NotificationApi;
|
|
14
15
|
private systemApi: SystemApi;
|
|
16
|
+
private proxyApi: ProxyApi;
|
|
15
17
|
|
|
16
18
|
constructor(store: Store<any>) {
|
|
17
19
|
this.modalApi = new ModalApiImpl(store);
|
|
18
20
|
this.slideInApi = new SlideInApiImpl(store);
|
|
19
21
|
this.notificationApi = new NotificationApiImpl(store);
|
|
20
22
|
this.systemApi = new SystemApiImpl(store);
|
|
23
|
+
this.proxyApi = new ProxyApiImpl(store);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
get modal(): ModalApi {
|
|
@@ -35,4 +38,8 @@ export class ShellApiImpl implements ShellApi {
|
|
|
35
38
|
get system(): SystemApi {
|
|
36
39
|
return this.systemApi;
|
|
37
40
|
}
|
|
41
|
+
|
|
42
|
+
get proxy(): ProxyApi {
|
|
43
|
+
return this.proxyApi;
|
|
44
|
+
}
|
|
38
45
|
}
|
package/apis/shell/modal.ts
CHANGED
|
@@ -14,9 +14,12 @@ export class ModalApiImpl implements ModalApi {
|
|
|
14
14
|
*
|
|
15
15
|
* Example:
|
|
16
16
|
* ```ts
|
|
17
|
+
* import { useShell } from '@shell/apis';
|
|
17
18
|
* import MyCustomModal from '@/components/MyCustomModal.vue';
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
+
* const shell = useShell();
|
|
21
|
+
*
|
|
22
|
+
* shell.modal.open(MyCustomModal, {
|
|
20
23
|
* props: { title: 'Hello Modal' }
|
|
21
24
|
* });
|
|
22
25
|
* ```
|
|
@@ -14,12 +14,13 @@ export class NotificationApiImpl implements NotificationApi {
|
|
|
14
14
|
*
|
|
15
15
|
* Example:
|
|
16
16
|
* ```ts
|
|
17
|
+
* import { useShell } from '@shell/apis';
|
|
17
18
|
* import { NotificationLevel } from '@shell/types/notifications';
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
* ```
|
|
20
|
+
* const shell = useShell();
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* shell.notification.send(NotificationLevel.Success, 'Some notification title', 'Hello world! Success!', {})
|
|
23
|
+
* ```
|
|
23
24
|
*
|
|
24
25
|
* @param level The `level` specifies the importance of the notification and determines the icon that is shown in the notification
|
|
25
26
|
* @param title The notification title
|
|
@@ -45,10 +46,12 @@ export class NotificationApiImpl implements NotificationApi {
|
|
|
45
46
|
*
|
|
46
47
|
* Example:
|
|
47
48
|
* ```ts
|
|
48
|
-
*
|
|
49
|
-
* ```
|
|
49
|
+
* import { useShell } from '@shell/apis';
|
|
50
50
|
*
|
|
51
|
-
*
|
|
51
|
+
* const shell = useShell();
|
|
52
|
+
*
|
|
53
|
+
* shell.notification.updateProgress('some-notification-id', 80)
|
|
54
|
+
* ```
|
|
52
55
|
*
|
|
53
56
|
* @param notificationId Unique ID for the notification
|
|
54
57
|
* @param progress Progress (0-100) for notifications of type `Task`
|