"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ariaLevel = exports.ariaFlowtoCandidate = void 0; exports.currentRelatedEventData = currentRelatedEventData; exports.detectedBounds = detectedBounds; exports.hadErrorLoadingNodeEventsInCategory = exports.graphableNodes = exports.graphNodeForID = exports.eventIndices = void 0; exports.hadErrorLoadingTree = hadErrorLoadingTree; exports.hasMoreGenerations = exports.hasMoreChildren = exports.hasMoreAncestors = void 0; exports.isCurrentRelatedEventLoading = isCurrentRelatedEventLoading; exports.isLoadingNodeEventsInCategory = exports.isLoadingMoreNodeEventsInCategory = void 0; exports.isTreeLoading = isTreeLoading; exports.relatedEventTotalCount = exports.relatedEventCountOfTypeForNode = exports.panelViewAndParameters = exports.originID = exports.nodesAndEdgelines = exports.nodeStats = exports.nodeEventsInCategory = exports.nodeDataStatus = exports.nodeDataForID = exports.layout = exports.lastRelatedEventResponseContainsCursor = void 0; exports.resolverComponentInstanceID = resolverComponentInstanceID; exports.resolverTreeHasNodes = void 0; exports.resolverTreeSourceAndSchema = resolverTreeSourceAndSchema; exports.treeParameterIndices = exports.totalRelatedEventCountForNode = exports.timeRangeFilters = exports.statsTotalForNode = void 0; exports.treeParametersToFetch = treeParametersToFetch; exports.treeRequestParametersToAbort = treeRequestParametersToAbort; var _rbush = _interopRequireDefault(require("rbush")); var _reselect = require("reselect"); var _panel_view_and_parameters = require("../panel_view_and_parameters"); var indexedProcessTreeModel = _interopRequireWildcard(require("../../models/indexed_process_tree")); var nodeModel = _interopRequireWildcard(require("../../../../common/endpoint/models/node")); var nodeEventsInCategoryModel = _interopRequireWildcard(require("./node_events_in_category_model")); var resolverTreeModel = _interopRequireWildcard(require("../../models/resolver_tree")); var treeFetcherParametersModel = _interopRequireWildcard(require("../../models/tree_fetcher_parameters")); var isometricTaxiLayoutModel = _interopRequireWildcard(require("../../models/indexed_process_tree/isometric_taxi_layout")); var timeRangeModel = _interopRequireWildcard(require("../../models/time_range")); var aabbModel = _interopRequireWildcard(require("../../models/aabb")); var vector2 = _interopRequireWildcard(require("../../models/vector2")); 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. */ /** * Was a request made for graph data */ function isTreeLoading(state) { var _state$tree; return ((_state$tree = state.tree) === null || _state$tree === void 0 ? void 0 : _state$tree.pendingRequestParameters) !== undefined; } function detectedBounds(state) { return state.detectedBounds; } /** * If a request was made and it threw an error or returned a failure response code. */ function hadErrorLoadingTree(state) { var _state$tree2; if ((_state$tree2 = state.tree) !== null && _state$tree2 !== void 0 && _state$tree2.lastResponse) { var _state$tree3; return !((_state$tree3 = state.tree) !== null && _state$tree3 !== void 0 && _state$tree3.lastResponse.successful); } return false; } /** * A string for uniquely identifying the instance of resolver within the app. */ function resolverComponentInstanceID(state) { return state.resolverComponentInstanceID ? state.resolverComponentInstanceID : ''; } /** * The indices resolver should use, passed in as external props. */ const currentIndices = state => { return state.indices; }; /** * The last NewResolverTree we received, if any. It may be stale (it might not be for the same databaseDocumentID that * we're currently interested in. */ const resolverTreeResponse = state => { var _state$tree4, _state$tree4$lastResp, _state$tree5; return (_state$tree4 = state.tree) !== null && _state$tree4 !== void 0 && (_state$tree4$lastResp = _state$tree4.lastResponse) !== null && _state$tree4$lastResp !== void 0 && _state$tree4$lastResp.successful ? (_state$tree5 = state.tree) === null || _state$tree5 === void 0 ? void 0 : _state$tree5.lastResponse.result : undefined; }; const resolverTreeHasNodes = state => { var _state$tree6, _state$tree6$lastResp, _state$tree7, _state$tree7$lastResp, _state$tree7$lastResp2; return (_state$tree6 = state.tree) !== null && _state$tree6 !== void 0 && (_state$tree6$lastResp = _state$tree6.lastResponse) !== null && _state$tree6$lastResp !== void 0 && _state$tree6$lastResp.successful ? ((_state$tree7 = state.tree) === null || _state$tree7 === void 0 ? void 0 : (_state$tree7$lastResp = _state$tree7.lastResponse) === null || _state$tree7$lastResp === void 0 ? void 0 : (_state$tree7$lastResp2 = _state$tree7$lastResp.result) === null || _state$tree7$lastResp2 === void 0 ? void 0 : _state$tree7$lastResp2.nodes.length) > 0 : false; }; exports.resolverTreeHasNodes = resolverTreeHasNodes; const lastResponseIndices = state => { var _state$tree8, _state$tree8$lastResp, _state$tree9, _state$tree9$lastResp, _state$tree9$lastResp2; return (_state$tree8 = state.tree) !== null && _state$tree8 !== void 0 && (_state$tree8$lastResp = _state$tree8.lastResponse) !== null && _state$tree8$lastResp !== void 0 && _state$tree8$lastResp.successful ? (_state$tree9 = state.tree) === null || _state$tree9 === void 0 ? void 0 : (_state$tree9$lastResp = _state$tree9.lastResponse) === null || _state$tree9$lastResp === void 0 ? void 0 : (_state$tree9$lastResp2 = _state$tree9$lastResp.parameters) === null || _state$tree9$lastResp2 === void 0 ? void 0 : _state$tree9$lastResp2.indices : undefined; }; /** * If we received a NewResolverTree, return the schema associated with that tree, otherwise return undefined. * As of writing, this is only used for the info popover in the graph_controls panel */ function resolverTreeSourceAndSchema(state) { var _state$tree10, _state$tree10$lastRes; if ((_state$tree10 = state.tree) !== null && _state$tree10 !== void 0 && (_state$tree10$lastRes = _state$tree10.lastResponse) !== null && _state$tree10$lastRes !== void 0 && _state$tree10$lastRes.successful) { var _state$tree11; const { schema, dataSource } = (_state$tree11 = state.tree) === null || _state$tree11 === void 0 ? void 0 : _state$tree11.lastResponse; return { schema, dataSource }; } return undefined; } /** * the node ID of the node representing the databaseDocumentID. * NB: this could be stale if the last response is stale */ const originID = (0, _reselect.createSelector)(resolverTreeResponse, function (resolverTree) { return resolverTree === null || resolverTree === void 0 ? void 0 : resolverTree.originID; }); /** * Returns a data structure for accessing events for specific nodes in a graph. For Endpoint graphs these nodes will be * process lifecycle events. */ exports.originID = originID; const nodeData = state => { return state.nodeData; }; /** * Returns a function that can be called to retrieve the node data for a specific node ID. */ const nodeDataForID = (0, _reselect.createSelector)(nodeData, nodeInfo => { return id => { const info = nodeInfo === null || nodeInfo === void 0 ? void 0 : nodeInfo.get(id); return info; }; }); /** * Returns a function that can be called to retrieve the state of the node, running, loading, or terminated. */ exports.nodeDataForID = nodeDataForID; const nodeDataStatus = (0, _reselect.createSelector)(nodeDataForID, nodeInfo => { return id => { const info = nodeInfo(id); if (!info) { return 'loading'; } return info.status; }; }); /** * Nodes that will be graphed. */ exports.nodeDataStatus = nodeDataStatus; const graphableNodes = (0, _reselect.createSelector)(resolverTreeResponse, function (treeResponse) { // Keep track of each unique nodeID const nodes = new Map(); if (treeResponse !== null && treeResponse !== void 0 && treeResponse.nodes) { for (const node of treeResponse.nodes) { const nodeID = nodeModel.nodeID(node); if (nodeID !== undefined) { nodes.set(nodeID, node); } } return [...nodes.values()]; } else { return []; } }); exports.graphableNodes = graphableNodes; const tree = (0, _reselect.createSelector)(graphableNodes, originID, function indexedProcessTree( // eslint-disable-next-line @typescript-eslint/no-shadow graphableNodes, currentOriginID) { return indexedProcessTreeModel.factory(graphableNodes, currentOriginID); }); /** * This returns a map of nodeIDs to the associated stats provided by the datasource. */ const nodeStats = (0, _reselect.createSelector)(resolverTreeResponse, resolverTree => { if (resolverTree) { const map = resolverTreeModel.nodeStats(resolverTree); return nodeID => map.get(nodeID); } else { return () => undefined; } }); /** * The total number of events related to a node. */ exports.nodeStats = nodeStats; const relatedEventTotalCount = (0, _reselect.createSelector)(nodeStats, getNodeStats => { return nodeID => { var _getNodeStats; return (_getNodeStats = getNodeStats(nodeID)) === null || _getNodeStats === void 0 ? void 0 : _getNodeStats.total; }; }); /** * Returns a boolean indicating if an even in the event_detail view is loading. * * @export * @param {DataState} state * @returns the loading state of the current related event data for the `event_detail` view */ exports.relatedEventTotalCount = relatedEventTotalCount; function isCurrentRelatedEventLoading(state) { return state.currentRelatedEvent.loading; } /** * Returns the current related event data for the `event_detail` view. * * @export * @param {DataState} state * @returns {(ResolverNode | null)} the current related event data for the `event_detail` view */ function currentRelatedEventData(state) { return state.currentRelatedEvent.data; } /** * Returns true if there might be more generations in the graph that we didn't get because we reached * the requested generations limit. * * If we set a limit at 10 and we received 9, then we know there weren't anymore. If we received 10 then there * might be more generations. */ const hasMoreGenerations = (0, _reselect.createSelector)(tree, resolverTreeSourceAndSchema, (resolverTree, sourceAndSchema) => { var _sourceAndSchema$sche, _resolverTree$generat; // if the ancestry field is defined then the server request will not be limited by the generations // field, so let's just assume that we always get all the generations we can, but we are instead // limited by the number of descendants to retrieve which is handled by a different selector if (sourceAndSchema !== null && sourceAndSchema !== void 0 && (_sourceAndSchema$sche = sourceAndSchema.schema) !== null && _sourceAndSchema$sche !== void 0 && _sourceAndSchema$sche.ancestry) { return false; } return ((_resolverTree$generat = resolverTree.generations) !== null && _resolverTree$generat !== void 0 ? _resolverTree$generat : 0) >= resolverTreeModel.generationsRequestAmount(sourceAndSchema === null || sourceAndSchema === void 0 ? void 0 : sourceAndSchema.schema); }); /** * Returns true if there might be more descendants in the graph that we didn't get because * we reached the requested descendants limit. * * If we set a limit at 10 and we received 9, then we know there weren't anymore. If we received * 10, there might be more. */ exports.hasMoreGenerations = hasMoreGenerations; const hasMoreChildren = (0, _reselect.createSelector)(tree, resolverTree => { var _resolverTree$descend; return ((_resolverTree$descend = resolverTree.descendants) !== null && _resolverTree$descend !== void 0 ? _resolverTree$descend : 0) >= resolverTreeModel.descendantsRequestAmount(); }); /** * Returns true if there might be more ancestors in the graph that we didn't get because * we reached the requested limit. * * If we set a limit at 10 and we received 9, then we know there weren't anymore. If we received * 10, there might be more. */ exports.hasMoreChildren = hasMoreChildren; const hasMoreAncestors = (0, _reselect.createSelector)(tree, resolverTreeSourceAndSchema, (resolverTree, sourceAndSchema) => { var _resolverTree$ancesto; return ((_resolverTree$ancesto = resolverTree.ancestors) !== null && _resolverTree$ancesto !== void 0 ? _resolverTree$ancesto : 0) >= resolverTreeModel.ancestorsRequestAmount(sourceAndSchema === null || sourceAndSchema === void 0 ? void 0 : sourceAndSchema.schema); }); /** * If the tree resource needs to be fetched then these are the parameters that should be used. */ exports.hasMoreAncestors = hasMoreAncestors; function treeParametersToFetch(state) { var _state$tree12, _state$tree13, _state$tree14, _state$tree14$lastRes, _state$tree15, _state$tree16; /** * If there are current tree parameters that don't match the parameters used in the pending request (if there is a pending request) and that don't match the parameters used in the last completed request (if there was a last completed request) then we need to fetch the tree resource using the current parameters. */ if (((_state$tree12 = state.tree) === null || _state$tree12 === void 0 ? void 0 : _state$tree12.currentParameters) !== undefined && !treeFetcherParametersModel.equal((_state$tree13 = state.tree) === null || _state$tree13 === void 0 ? void 0 : _state$tree13.currentParameters, (_state$tree14 = state.tree) === null || _state$tree14 === void 0 ? void 0 : (_state$tree14$lastRes = _state$tree14.lastResponse) === null || _state$tree14$lastRes === void 0 ? void 0 : _state$tree14$lastRes.parameters) && !treeFetcherParametersModel.equal((_state$tree15 = state.tree) === null || _state$tree15 === void 0 ? void 0 : _state$tree15.currentParameters, (_state$tree16 = state.tree) === null || _state$tree16 === void 0 ? void 0 : _state$tree16.pendingRequestParameters)) { return state.tree.currentParameters; } else { return null; } } /** * Retrieve the time range filters if they exist, otherwise default to start of epoch to the largest future date. */ const timeRangeFilters = (0, _reselect.createSelector)(state => { var _state$tree17; return (_state$tree17 = state.tree) === null || _state$tree17 === void 0 ? void 0 : _state$tree17.currentParameters; }, function timeRangeFilters(treeParameters) { // Should always be provided from date picker, but provide valid defaults in any case. const from = new Date(0); const to = new Date(timeRangeModel.maxDate); const timeRange = { from: from.toISOString(), to: to.toISOString() }; if (treeParameters !== undefined) { if (treeParameters.filters.from) { timeRange.from = treeParameters.filters.from; } if (treeParameters.filters.to) { timeRange.to = treeParameters.filters.to; } } return timeRange; }); /** * The indices to use for the requests with the backend. */ exports.timeRangeFilters = timeRangeFilters; const treeParameterIndices = (0, _reselect.createSelector)(treeParametersToFetch, parameters => { var _parameters$indices; return (_parameters$indices = parameters === null || parameters === void 0 ? void 0 : parameters.indices) !== null && _parameters$indices !== void 0 ? _parameters$indices : []; }); /** * Panel requests should not use indices derived from the tree parameter selector, as this is only defined briefly while the resolver_tree_fetcher middleware is running. * Instead, panel requests should use the indices used by the last good request, falling back to the indices passed as external props. */ exports.treeParameterIndices = treeParameterIndices; const eventIndices = (0, _reselect.createSelector)(lastResponseIndices, currentIndices, function eventIndices(lastIndices, current) { var _ref; return (_ref = lastIndices !== null && lastIndices !== void 0 ? lastIndices : current) !== null && _ref !== void 0 ? _ref : []; }); exports.eventIndices = eventIndices; const layout = (0, _reselect.createSelector)(tree, originID, function processNodePositionsAndEdgeLineSegments(indexedProcessTree, currentOriginID) { // use the isometric taxi layout as a base const taxiLayout = isometricTaxiLayoutModel.isometricTaxiLayoutFactory(indexedProcessTree); if (!currentOriginID) { // no data has loaded. return taxiLayout; } // find the origin node const originNode = indexedProcessTreeModel.treeNode(indexedProcessTree, currentOriginID); if (originNode === null) { // If a tree is returned that has no process events for the origin, this can happen. return taxiLayout; } // Find the position of the origin, we'll center the map on it intrinsically const originPosition = isometricTaxiLayoutModel.nodePosition(taxiLayout, currentOriginID); // adjust the position of everything so that the origin node is at `(0, 0)` if (originPosition === undefined) { // not sure how this could happen. return taxiLayout; } // Take the origin position, and multipy it by -1, then move the layout by that amount. // This should center the layout around the origin. return isometricTaxiLayoutModel.translated(taxiLayout, vector2.scale(originPosition, -1)); }); /** * Given a nodeID (aka entity_id) get the indexed process event. * Legacy functions take process events instead of nodeID, use this to get * process events for them. */ exports.layout = layout; const graphNodeForID = (0, _reselect.createSelector)(tree, indexedProcessTree => nodeID => { return indexedProcessTreeModel.treeNode(indexedProcessTree, nodeID); }); /** * Takes a nodeID (aka entity_id) and returns the associated aria level as a number or null if the node ID isn't in the tree. */ exports.graphNodeForID = graphNodeForID; const ariaLevel = (0, _reselect.createSelector)(layout, graphNodeForID, ({ ariaLevels }, graphNodeGetter) => nodeID => { var _ariaLevels$get; const node = graphNodeGetter(nodeID); return node ? (_ariaLevels$get = ariaLevels.get(node)) !== null && _ariaLevels$get !== void 0 ? _ariaLevels$get : null : null; }); /** * Returns the following sibling if there is one, or `null` if there isn't. * For root nodes, other root nodes are treated as siblings. * This is used to calculate the `aria-flowto` attribute. */ exports.ariaLevel = ariaLevel; const ariaFlowtoCandidate = (0, _reselect.createSelector)(tree, graphNodeForID, (indexedProcessTree, nodeGetter) => { // A map of preceding sibling IDs to following sibling IDs or `null`, if there is no following sibling const memo = new Map(); return function memoizedGetter( /** the unique ID of a node. **/nodeID) { // Previous calculations are memoized. Check for a value in the memo. const existingValue = memo.get(nodeID); /** * `undefined` means the key wasn't in the map. * Note: the value may be null, meaning that we checked and there is no following sibling. * If there is a value in the map, return it. */ if (existingValue !== undefined) { return existingValue; } /** * Getting the following sibling of a node has an `O(n)` time complexity where `n` is the number of children the parent of the node has. * For this reason, we calculate the following siblings of the node and all of its siblings at once and cache them. */ const node = nodeGetter(nodeID); if (!node) { // this should never happen. throw new Error('could not find child event in process tree.'); } // nodes with the same parent ID const children = indexedProcessTreeModel.children(indexedProcessTree, nodeModel.parentId(node)); let previousChild = null; // Loop over all nodes that have the same parent ID (even if the parent ID is undefined or points to a node that isn't in the tree.) for (const child of children) { if (previousChild !== null) { // Set the `child` as the following sibling of `previousChild`. const previousChildNodeId = nodeModel.nodeID(previousChild); const followingSiblingEntityID = nodeModel.nodeID(child); if (previousChildNodeId !== undefined && followingSiblingEntityID !== undefined) { memo.set(previousChildNodeId, followingSiblingEntityID); } } // Set the child as the previous child. previousChild = child; } if (previousChild) { // if there is a previous child, it has no following sibling. const previousChildNodeID = nodeModel.nodeID(previousChild); if (previousChildNodeID !== undefined) { memo.set(previousChildNodeID, null); } } return memoizedGetter(nodeID); }; }); exports.ariaFlowtoCandidate = ariaFlowtoCandidate; const spatiallyIndexedLayout = (0, _reselect.createSelector)(layout, function ({ processNodePositions, edgeLineSegments }) { const spatialIndex = new _rbush.default(); const nodeToIndex = []; const edgeLineSegmentsToIndex = []; // Make sure these numbers are big enough to cover the process nodes at all zoom levels. // The process nodes don't extend equally in all directions from their center point. const graphNodeViewWidth = 720; const graphNodeViewHeight = 240; const lineSegmentPadding = 30; for (const [treeNode, position] of processNodePositions) { const [nodeX, nodeY] = position; const indexedEvent = { minX: nodeX - 0.5 * graphNodeViewWidth, minY: nodeY - 0.5 * graphNodeViewHeight, maxX: nodeX + 0.5 * graphNodeViewWidth, maxY: nodeY + 0.5 * graphNodeViewHeight, position, entity: treeNode, type: 'treeNode' }; nodeToIndex.push(indexedEvent); } for (const edgeLineSegment of edgeLineSegments) { const { points: [[x1, y1], [x2, y2]] } = edgeLineSegment; const indexedLineSegment = { minX: Math.min(x1, x2) - lineSegmentPadding, minY: Math.min(y1, y2) - lineSegmentPadding, maxX: Math.max(x1, x2) + lineSegmentPadding, maxY: Math.max(y1, y2) + lineSegmentPadding, entity: edgeLineSegment, type: 'edgeLine' }; edgeLineSegmentsToIndex.push(indexedLineSegment); } spatialIndex.load([...nodeToIndex, ...edgeLineSegmentsToIndex]); return spatialIndex; }); /** * Returns nodes and edge lines that could be visible in the `query`. */ const nodesAndEdgelines = (0, _reselect.createSelector)(spatiallyIndexedLayout, function (spatialIndex) { /** * Memoized for performance and object reference equality. */ return (0, _reselect.defaultMemoize)(boundingBox => { const { minimum: [minX, minY], maximum: [maxX, maxY] } = boundingBox; const entities = spatialIndex.search({ minX, minY, maxX, maxY }); const visibleProcessNodePositions = new Map(entities.filter(entity => entity.type === 'treeNode').map(node => [node.entity, node.position])); const connectingEdgeLineSegments = entities.filter(entity => entity.type === 'edgeLine').map(node => node.entity); return { processNodePositions: visibleProcessNodePositions, connectingEdgeLineSegments }; }, aabbModel.isEqual); }); /** * If there is a pending request that's for a entity ID that doesn't matche the `entityID`, then we should cancel it. */ exports.nodesAndEdgelines = nodesAndEdgelines; function treeRequestParametersToAbort(state) { var _state$tree18, _state$tree19, _state$tree20; /** * If there is a pending request, and its not for the current parameters (even, if the current parameters are undefined) then we should abort the request. */ if (((_state$tree18 = state.tree) === null || _state$tree18 === void 0 ? void 0 : _state$tree18.pendingRequestParameters) !== undefined && !treeFetcherParametersModel.equal((_state$tree19 = state.tree) === null || _state$tree19 === void 0 ? void 0 : _state$tree19.pendingRequestParameters, (_state$tree20 = state.tree) === null || _state$tree20 === void 0 ? void 0 : _state$tree20.currentParameters)) { return state.tree.pendingRequestParameters; } else { return null; } } /** * The sum of all related event categories for a process. */ const statsTotalForNode = (0, _reselect.createSelector)(nodeStats, getNodeStats => { return node => { const nodeID = nodeModel.nodeID(node); if (nodeID === undefined) { return null; } const stats = getNodeStats(nodeID); if (!stats) { return null; } return stats.total; }; }); /** * Total count of events related to `node`. * Based on `ResolverNodeStats` */ exports.statsTotalForNode = statsTotalForNode; const totalRelatedEventCountForNode = (0, _reselect.createSelector)(nodeStats, getNodeStats => nodeID => { const stats = getNodeStats(nodeID); return stats === undefined ? undefined : stats.total; }); /** * Count of events with `category` related to `nodeID`. * Based on `ResolverNodeStats` */ exports.totalRelatedEventCountForNode = totalRelatedEventCountForNode; const relatedEventCountOfTypeForNode = (0, _reselect.createSelector)(nodeStats, getNodeStats => (nodeID, category) => { const stats = getNodeStats(nodeID); if (!stats) { return undefined; } else { return stats.byCategory[category]; } }); /** * Which view should show in the panel, as well as what parameters should be used. * Calculated using the query string */ exports.relatedEventCountOfTypeForNode = relatedEventCountOfTypeForNode; const panelViewAndParameters = (0, _reselect.createSelector)(state => state.locationSearch, resolverComponentInstanceID, // eslint-disable-next-line @typescript-eslint/no-shadow (locationSearch, resolverComponentInstanceID) => { return (0, _panel_view_and_parameters.panelViewAndParameters)({ locationSearch, resolverComponentInstanceID }); }); /** * Events related to the panel node that are in the panel category. * NB: This cannot tell the view loading information. For example, this does not tell the view if data has been requested or if data failed to load. */ exports.panelViewAndParameters = panelViewAndParameters; const nodeEventsInCategory = state => { var _state$nodeEventsInCa, _state$nodeEventsInCa2; return (_state$nodeEventsInCa = (_state$nodeEventsInCa2 = state.nodeEventsInCategory) === null || _state$nodeEventsInCa2 === void 0 ? void 0 : _state$nodeEventsInCa2.events) !== null && _state$nodeEventsInCa !== void 0 ? _state$nodeEventsInCa : []; }; exports.nodeEventsInCategory = nodeEventsInCategory; const lastRelatedEventResponseContainsCursor = (0, _reselect.createSelector)(state => state.nodeEventsInCategory, panelViewAndParameters, // eslint-disable-next-line @typescript-eslint/no-shadow function (nodeEventsInCategory, panelViewAndParameters) { if (nodeEventsInCategory !== undefined && nodeEventsInCategoryModel.isRelevantToPanelViewAndParameters(nodeEventsInCategory, panelViewAndParameters)) { return nodeEventsInCategory.cursor !== null; } else { return false; } }); exports.lastRelatedEventResponseContainsCursor = lastRelatedEventResponseContainsCursor; const hadErrorLoadingNodeEventsInCategory = (0, _reselect.createSelector)(state => state.nodeEventsInCategory, panelViewAndParameters, // eslint-disable-next-line @typescript-eslint/no-shadow function (nodeEventsInCategory, panelViewAndParameters) { if (nodeEventsInCategory !== undefined && nodeEventsInCategoryModel.isRelevantToPanelViewAndParameters(nodeEventsInCategory, panelViewAndParameters)) { return nodeEventsInCategory && nodeEventsInCategory.error === true; } else { return false; } }); exports.hadErrorLoadingNodeEventsInCategory = hadErrorLoadingNodeEventsInCategory; const isLoadingNodeEventsInCategory = (0, _reselect.createSelector)(state => state.nodeEventsInCategory, panelViewAndParameters, // eslint-disable-next-line @typescript-eslint/no-shadow function (nodeEventsInCategory, panelViewAndParameters) { const { panelView } = panelViewAndParameters; return panelView === 'nodeEventsInCategory' && nodeEventsInCategory === undefined; }); exports.isLoadingNodeEventsInCategory = isLoadingNodeEventsInCategory; const isLoadingMoreNodeEventsInCategory = (0, _reselect.createSelector)(state => state.nodeEventsInCategory, panelViewAndParameters, // eslint-disable-next-line @typescript-eslint/no-shadow function (nodeEventsInCategory, panelViewAndParameters) { if (nodeEventsInCategory !== undefined && nodeEventsInCategoryModel.isRelevantToPanelViewAndParameters(nodeEventsInCategory, panelViewAndParameters)) { return nodeEventsInCategory && nodeEventsInCategory.lastCursorRequested !== null && nodeEventsInCategory.cursor === nodeEventsInCategory.lastCursorRequested; } else { return false; } }); exports.isLoadingMoreNodeEventsInCategory = isLoadingMoreNodeEventsInCategory;