"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useMetricsExplorerViews = void 0; var rt = _interopRequireWildcard(require("io-ts")); var _pipeable = require("fp-ts/lib/pipeable"); var _Either = require("fp-ts/lib/Either"); var _function = require("fp-ts/lib/function"); var _reactQuery = require("@tanstack/react-query"); var _public = require("@kbn/observability-shared-plugin/public"); var _use_kibana = require("./use_kibana"); var _use_url_state = require("../utils/use_url_state"); var _use_saved_views_notifier = require("./use_saved_views_notifier"); var _metrics_source = require("../containers/metrics_source"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const queryKeys = { find: ['metrics-explorer-views-find'], get: ['metrics-explorer-views-get'], getById: id => ['metrics-explorer-views-get', id] }; const useMetricsExplorerViews = () => { var _source$configuration; const { metricsExplorerViews } = (0, _use_kibana.useKibanaContextForPlugin)().services; const trackMetric = (0, _public.useUiTracker)({ app: 'infra_metrics' }); const queryClient = (0, _reactQuery.useQueryClient)(); const { source, updateSourceConfiguration } = (0, _metrics_source.useSourceContext)(); const defaultViewId = (_source$configuration = source === null || source === void 0 ? void 0 : source.configuration.metricsExplorerDefaultView) !== null && _source$configuration !== void 0 ? _source$configuration : '0'; const [currentViewId, switchViewById] = (0, _use_url_state.useUrlState)({ defaultState: defaultViewId, decodeUrlState, encodeUrlState, urlStateKey: 'metricsExplorerViewId', writeDefaultState: true }); const notify = (0, _use_saved_views_notifier.useSavedViewsNotifier)(); const { data: views, refetch: fetchViews, isFetching: isFetchingViews } = (0, _reactQuery.useQuery)({ queryKey: queryKeys.find, queryFn: () => metricsExplorerViews.client.findMetricsExplorerViews(), enabled: false, // We will manually fetch the list when necessary placeholderData: [], // Use a default empty array instead of undefined onError: error => { var _error$body$message, _error$body; return notify.getViewFailure((_error$body$message = (_error$body = error.body) === null || _error$body === void 0 ? void 0 : _error$body.message) !== null && _error$body$message !== void 0 ? _error$body$message : error.message); }, onSuccess: data => { const prefix = data.length >= 1000 ? 'over' : 'under'; trackMetric({ metric: `${prefix}_1000_saved_objects_for_metrics_explorer_view` }); } }); const { data: currentView, isFetching: isFetchingCurrentView } = (0, _reactQuery.useQuery)({ queryKey: queryKeys.getById(currentViewId), queryFn: ({ queryKey: [, id] }) => metricsExplorerViews.client.getMetricsExplorerView(id), onError: error => { var _error$body$message2, _error$body2; notify.getViewFailure((_error$body$message2 = (_error$body2 = error.body) === null || _error$body2 === void 0 ? void 0 : _error$body2.message) !== null && _error$body$message2 !== void 0 ? _error$body$message2 : error.message); switchViewById(defaultViewId); }, placeholderData: null }); const { mutate: setDefaultViewById } = (0, _reactQuery.useMutation)({ mutationFn: id => updateSourceConfiguration({ metricsExplorerDefaultView: id }), /** * To provide a quick feedback, we perform an optimistic update on the list * when updating the default view. * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) * 2. Snapshot the previous views list * 3. Optimistically update the list with new default view and store in cache * 4. Return a context object with the snapshotted views */ onMutate: async id => { await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 const previousViews = queryClient.getQueryData(queryKeys.find); // 2 const updatedViews = getListWithUpdatedDefault(id, previousViews); // 3 queryClient.setQueryData(queryKeys.find, updatedViews); return { previousViews }; // 4 }, // If the mutation fails but doesn't retrigger the error, use the context returned from onMutate to roll back onSuccess: (data, _id, context) => { if (!data && context !== null && context !== void 0 && context.previousViews) { return queryClient.setQueryData(queryKeys.find, context.previousViews); } return queryClient.invalidateQueries({ queryKey: queryKeys.get }); } }); const { mutateAsync: createView, isLoading: isCreatingView } = (0, _reactQuery.useMutation)({ mutationFn: attributes => metricsExplorerViews.client.createMetricsExplorerView(attributes), onError: error => { var _error$body$message3, _error$body3; notify.upsertViewFailure((_error$body$message3 = (_error$body3 = error.body) === null || _error$body3 === void 0 ? void 0 : _error$body3.message) !== null && _error$body$message3 !== void 0 ? _error$body$message3 : error.message); }, onSuccess: createdView => { queryClient.setQueryData(queryKeys.getById(createdView.id), createdView); // Store in cache created view switchViewById(createdView.id); // Update current view and url state } }); const { mutateAsync: updateViewById, isLoading: isUpdatingView } = (0, _reactQuery.useMutation)({ mutationFn: ({ id, attributes }) => metricsExplorerViews.client.updateMetricsExplorerView(id, attributes), onError: error => { var _error$body$message4, _error$body4; notify.upsertViewFailure((_error$body$message4 = (_error$body4 = error.body) === null || _error$body4 === void 0 ? void 0 : _error$body4.message) !== null && _error$body$message4 !== void 0 ? _error$body$message4 : error.message); }, onSuccess: updatedView => { queryClient.setQueryData(queryKeys.getById(updatedView.id), updatedView); // Store in cache updated view } }); const { mutate: deleteViewById } = (0, _reactQuery.useMutation)({ mutationFn: id => metricsExplorerViews.client.deleteMetricsExplorerView(id), /** * To provide a quick feedback, we perform an optimistic update on the list * when deleting a view. * 1. Cancel any outgoing refetches (so they don't overwrite our optimistic update) * 2. Snapshot the previous views list * 3. Optimistically update the list removing the view and store in cache * 4. Return a context object with the snapshotted views */ onMutate: async id => { await queryClient.cancelQueries({ queryKey: queryKeys.find }); // 1 const previousViews = queryClient.getQueryData(queryKeys.find); // 2 const updatedViews = getListWithoutDeletedView(id, previousViews); // 3 queryClient.setQueryData(queryKeys.find, updatedViews); return { previousViews }; // 4 }, // If the mutation fails, use the context returned from onMutate to roll back onError: (error, _id, context) => { var _error$body$message5, _error$body5; notify.deleteViewFailure((_error$body$message5 = (_error$body5 = error.body) === null || _error$body5 === void 0 ? void 0 : _error$body5.message) !== null && _error$body$message5 !== void 0 ? _error$body$message5 : error.message); if (context !== null && context !== void 0 && context.previousViews) { queryClient.setQueryData(queryKeys.find, context.previousViews); } }, onSuccess: (_data, id) => { // If the deleted view was the current one, switch to the default view if ((currentView === null || currentView === void 0 ? void 0 : currentView.id) === id) { switchViewById(defaultViewId); } }, onSettled: () => { fetchViews(); // Invalidate views list cache and refetch views } }); return { // Values views, currentView, // Actions about updating view createView, deleteViewById, fetchViews, updateViewById, switchViewById, setDefaultViewById, // Status flags isCreatingView, isFetchingCurrentView, isFetchingViews, isUpdatingView }; }; exports.useMetricsExplorerViews = useMetricsExplorerViews; const metricsExplorerViewIdRT = rt.string; const encodeUrlState = metricsExplorerViewIdRT.encode; const decodeUrlState = value => { const state = (0, _pipeable.pipe)(metricsExplorerViewIdRT.decode(value), (0, _Either.fold)((0, _function.constant)(undefined), _function.identity)); return state; }; /** * Helpers */ const getListWithUpdatedDefault = (id, views = []) => { return views.map(view => ({ ...view, attributes: { ...view.attributes, isDefault: view.id === id } })); }; const getListWithoutDeletedView = (id, views = []) => { return views.filter(view => view.id !== id); };