UNPKG

@stolostron/multicluster-sdk

Version:

Provides extensions and APIs that dynamic plugins can use to leverage multicluster capabilities provided by Red Hat Advanced Cluster Management.

190 lines 9.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useFleetSearchPoll = void 0; /* Copyright Contributors to the Open Cluster Management project */ const search_sdk_1 = require("../internal/search/search-sdk"); const react_1 = require("react"); const search_client_1 = require("../internal/search/search-client"); // Constants for polling interval configuration const DEFAULT_POLL_INTERVAL_SECONDS = 30; /** * A React hook that provides fleet-wide search functionality using the ACM search API. * * @template T - The type of Kubernetes resource(s) to search for, extending K8sResourceCommon * * @param watchOptions - Configuration options for the resource watch * @param watchOptions.groupVersionKind - The group, version, and kind of the resource to search for * @param watchOptions.limit - Maximum number of results to return (defaults to -1 for no limit) * @param watchOptions.namespace - Namespace to search in (only used if namespaced is true) * @param watchOptions.namespaced - Whether the resource is namespaced * @param watchOptions.name - Specific resource name to search for (exact match) * @param watchOptions.isList - Whether to return results as a list or single item * * @param advancedSearch - Optional array of additional search filters * @param advancedSearch[].property - The property name to filter on * @param advancedSearch[].values - Array of values to match for the property * * @param pollInterval - Optional polling interval in seconds. Defaults to 30 seconds (polling enabled). * - Not specified: polls every 30 seconds * - 0-30 inclusive: polls every 30 seconds (minimum interval) * - >30: polls at the given interval in seconds * - false or negative: disables polling * * @returns A tuple containing: * - `data`: The search results formatted as Kubernetes resources, or undefined if no results * - `loaded`: Boolean indicating if the search has completed (opposite of loading) * - `error`: Any error that occurred during the search, or undefined if successful * - `refetch`: A callback that enables you to re-execute the query * * @example * ```typescript * // Search for all Pods in a specific namespace with default 30-second polling * const [pods, loaded, error] = useFleetSearchPoll({ * groupVersionKind: { group: '', version: 'v1', kind: 'Pod' }, * namespace: 'default', * namespaced: true, * isList: true * }); * * // Search for a specific Deployment with polling every 60 seconds * const [deployment, loaded, error] = useFleetSearchPoll({ * groupVersionKind: { group: 'apps', version: 'v1', kind: 'Deployment' }, * name: 'my-deployment', * namespace: 'default', * namespaced: true, * isList: false * }, [ * { property: 'label', values: ['app=my-app'] } * ], 60); * * // Search without polling (one-time query) * const [services, loaded, error] = useFleetSearchPoll({ * groupVersionKind: { group: '', version: 'v1', kind: 'Service' }, * namespaced: true, * isList: true * }, undefined, false); * ``` * * @remarks * - The hook automatically handles the transformation of flattened search results back into * properly structured Kubernetes resources * - Special handling is provided for VirtualMachine and VirtualMachineInstance resources * - Watch options filters take precedence over advanced search filters * - The search is skipped if no `kind` is specified in the groupVersionKind * - Results include cluster information for multi-cluster environments * - Polling is enabled by default with a 30-second interval; use false to disable * - Minimum polling interval is 30 seconds for performance reasons */ const useFleetSearchPoll = (watchOptions, advancedSearch, pollInterval) => { var _a; const { groupVersionKind, limit, namespace, namespaced, name, isList } = watchOptions; const { group, version, kind } = groupVersionKind !== null && groupVersionKind !== void 0 ? groupVersionKind : {}; // Calculate the actual polling interval in milliseconds const actualPollInterval = (0, react_1.useMemo)(() => { // Disable polling for false or negative values if (pollInterval === false || (typeof pollInterval === 'number' && pollInterval < 0)) { return undefined; } // Default to 30 seconds if not specified, or use minimum of 30 seconds for specified values const intervalInSeconds = pollInterval === undefined || pollInterval <= DEFAULT_POLL_INTERVAL_SECONDS ? DEFAULT_POLL_INTERVAL_SECONDS : pollInterval; return intervalInSeconds * 1000; }, [pollInterval]); const searchInput = (0, react_1.useMemo)(() => { const filters = []; // Add filters from watchOptions (these take precedence) const watchOptionsProperties = new Set(); if (group) { filters.push({ property: 'apigroup', values: [group] }); watchOptionsProperties.add('apigroup'); } if (version) { filters.push({ property: 'apiversion', values: [version] }); watchOptionsProperties.add('apiversion'); } if (kind) { filters.push({ property: 'kind', values: [kind] }); watchOptionsProperties.add('kind'); } if (namespaced && namespace) { filters.push({ property: 'namespace', values: [namespace] }); watchOptionsProperties.add('namespace'); } if (name && name.trim()) { // Use exact match instead of wildcard filters.push({ property: 'name', values: [name] }); watchOptionsProperties.add('name'); } // Add filters from advancedSearch, excluding properties already specified in watchOptions if (advancedSearch) { for (const { property, values } of advancedSearch) { if (property && values !== undefined && !watchOptionsProperties.has(property)) { filters.push({ property, values }); } } } return { filters, limit: limit !== null && limit !== void 0 ? limit : -1, }; }, [group, version, kind, namespaced, namespace, name, advancedSearch, limit]); const { data: result, loading, error, refetch, } = (0, search_sdk_1.useSearchResultItemsQuery)({ client: search_client_1.searchClient, skip: kind === undefined, pollInterval: actualPollInterval, variables: { input: [searchInput], }, }); const triggerRefetch = (0, react_1.useCallback)(() => { refetch(); }, [refetch]); const data = (0, react_1.useMemo)(() => { var _a, _b, _c; return (_c = (_b = (_a = result === null || result === void 0 ? void 0 : result.searchResult) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.items) === null || _c === void 0 ? void 0 : _c.map((item) => { let label = {}; if (item === null || item === void 0 ? void 0 : item.label) { label = Object.fromEntries(item.label.split(';').map((pair) => pair.trimStart().split('='))); } const resource = { cluster: item.cluster, apiVersion: `${item.apigroup ? `${item.apigroup}/` : ''}${item.apiversion}`, kind: item.kind, metadata: { creationTimestamp: item.created, name: item.name, namespace: item.namespace, labels: label, }, }; // Reverse the flattening of specific resources by the search-collector // See https://github.com/stolostron/search-collector/blob/main/pkg/transforms/genericResourceConfig.go switch (kind) { case 'VirtualMachine': resource.spec = { running: item._specRunning, runStrategy: item._specRunStrategy, template: { spec: { domain: { cpu: { cores: item.cpu }, memory: { guest: item.memory } } } }, }; resource.status = { conditions: [{ type: 'Ready', status: item.ready }], printableStatus: item.status }; break; case 'VirtualMachineInstance': resource.status = { conditions: [ { type: 'LiveMigratable', status: item.liveMigratable }, { type: 'Ready', status: item.ready }, ], interfaces: [{ ipAddress: item.ipaddress, name: 'default' }], nodeName: item.node, phase: item.phase, }; } return resource; }); }, [kind, result]); const nullResponse = (0, react_1.useMemo)(() => (isList ? [] : undefined), [isList]); return [(_a = data) !== null && _a !== void 0 ? _a : nullResponse, !loading, error, triggerRefetch]; }; exports.useFleetSearchPoll = useFleetSearchPoll; //# sourceMappingURL=useFleetSearchPoll.js.map