private async getAnalyticsMap()

in x-pack/platform/plugins/shared/ml/server/models/data_frame_analytics/analytics_manager.ts [348:570]


  private async getAnalyticsMap({
    analyticsId,
    modelId,
  }: GetAnalyticsMapArgs): Promise<AnalyticsMapReturnType> {
    const result: AnalyticsMapReturnType = { elements: [], details: {}, error: null };
    const modelElements: MapElements[] = [];
    const indexPatternElements: MapElements[] = [];

    try {
      await this.initData();
      // Create first node for incoming analyticsId or modelId
      let initialData: InitialElementsReturnType = Object.create(null) as InitialElementsReturnType;
      const job = analyticsId === undefined ? undefined : this.findJob(analyticsId);
      if (analyticsId !== undefined && job !== undefined) {
        const jobCreateTime = job.create_time!;
        initialData = await this.getInitialElementsJobRoot(analyticsId, jobCreateTime);
      } else if (modelId !== undefined) {
        initialData = await this.getInitialElementsModelRoot(modelId);
      }

      const {
        resultElements,
        details: initialDetails,
        modelElements: initialModelElements,
      } = initialData;

      result.elements.push(...resultElements);
      result.details = initialDetails;
      modelElements.push(...initialModelElements);

      if (isCompleteInitialReturnType(initialData)) {
        let { data, nextLinkId, nextType, previousNodeId } = initialData;

        let complete = false;
        let link: NextLinkReturnType;
        let count = 0;
        let rootTransform;
        let rootIndexPattern;
        let modelElement;
        let modelDetails;
        let edgeElement;

        // Add a safeguard against infinite loops.
        while (complete === false) {
          count++;
          if (count >= 100) {
            break;
          }

          try {
            link = await this.getNextLink({
              id: nextLinkId,
              type: nextType,
            });
          } catch (error) {
            result.error = error.message || 'Something went wrong';
            break;
          }
          // If it's index pattern, check meta data to see what to fetch next
          if (isIndexPatternLinkReturnType(link) && link.isIndexPattern === true) {
            if (link.isWildcardIndexPattern === true) {
              // Create index nodes for each of the indices included in the index pattern then break
              const { details, elements } = this.getIndexPatternElements(
                link.indexData,
                previousNodeId
              );

              indexPatternElements.push(...elements);
              result.details = { ...result.details, ...details };
              complete = true;
            } else {
              const nodeId = `${nextLinkId}-${JOB_MAP_NODE_TYPES.INDEX}`;
              result.elements.unshift({
                data: { id: nodeId, label: nextLinkId, type: JOB_MAP_NODE_TYPES.INDEX },
              });
              result.details[nodeId] = link.indexData;
            }

            // Check meta data
            if (
              link.isWildcardIndexPattern === false &&
              (link.meta === undefined ||
                link.meta?.created_by.includes(INDEX_CREATED_BY.FILE_DATA_VISUALIZER))
            ) {
              rootIndexPattern = nextLinkId;
              complete = true;
              break;
            }

            if (link.meta?.created_by === INDEX_CREATED_BY.DATA_FRAME_ANALYTICS) {
              nextLinkId = link.meta.analytics;
              nextType = JOB_MAP_NODE_TYPES.ANALYTICS;
            }

            if (link.meta?.created_by === JOB_MAP_NODE_TYPES.TRANSFORM) {
              nextLinkId = link.meta._transform?.transform;
              nextType = JOB_MAP_NODE_TYPES.TRANSFORM;
            }
          } else if (isJobDataLinkReturnType(link) && link.isJob === true) {
            // Create missing job node here if job is undefined
            data = link.jobData;
            const nodeId = `${data?.id ?? nextLinkId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`;
            previousNodeId = nodeId;

            result.elements.unshift(
              data === undefined
                ? this.getMissingJobNode(nextLinkId)
                : {
                    data: {
                      id: nodeId,
                      label: data.id,
                      type: JOB_MAP_NODE_TYPES.ANALYTICS,
                      analysisType: getAnalysisType(data?.analysis),
                    },
                  }
            );
            result.details[nodeId] = data;
            nextLinkId = data?.source?.index[0];
            nextType = JOB_MAP_NODE_TYPES.INDEX;

            if (data) {
              // Get trained model for analytics job and create model node
              ({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements(
                data.id,
                data.create_time
              ));
              if (isAnalyticsMapNodeElement(modelElement)) {
                modelElements.push(modelElement);
                result.details[modelElement.data.id] = modelDetails;
              }
              if (isAnalyticsMapEdgeElement(edgeElement)) {
                modelElements.push(edgeElement);
              }
            }
          } else if (isTransformLinkReturnType(link) && link.isTransform === true) {
            data = link.transformData;

            const nodeId = `${data.id}-${JOB_MAP_NODE_TYPES.TRANSFORM}`;
            previousNodeId = nodeId;
            rootTransform = data.dest.index;

            result.elements.unshift({
              data: { id: nodeId, label: data.id, type: JOB_MAP_NODE_TYPES.TRANSFORM },
            });
            result.details[nodeId] = data;
            nextLinkId = data?.source?.index[0];
            nextType = JOB_MAP_NODE_TYPES.INDEX;
          }
        } // end while

        // create edge elements
        const elemLength = result.elements.length - 1;
        for (let i = 0; i < elemLength; i++) {
          const currentElem = result.elements[i];
          const nextElem = result.elements[i + 1];
          if (
            currentElem !== undefined &&
            nextElem !== undefined &&
            currentElem?.data?.id.includes('*') === false &&
            nextElem?.data?.id.includes('*') === false
          ) {
            result.elements.push({
              data: {
                id: `${currentElem.data.id}~${nextElem.data.id}`,
                source: currentElem.data.id,
                target: nextElem.data.id,
              },
            });
          }
        }

        // fetch all jobs associated with root transform if defined, otherwise check root index
        if (rootTransform !== undefined || rootIndexPattern !== undefined) {
          const jobs = this._jobs;
          const comparator = rootTransform !== undefined ? rootTransform : rootIndexPattern;

          for (let i = 0; i < jobs.length; i++) {
            if (
              jobs[i]?.source?.index[0] === comparator &&
              this.isDuplicateElement(jobs[i].id, result.elements) === false
            ) {
              const nodeId = `${jobs[i].id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`;
              result.elements.push({
                data: {
                  id: nodeId,
                  label: jobs[i].id,
                  type: JOB_MAP_NODE_TYPES.ANALYTICS,
                  analysisType: getAnalysisType(jobs[i]?.analysis),
                },
              });
              result.details[nodeId] = jobs[i];
              const source = `${comparator}-${JOB_MAP_NODE_TYPES.INDEX}`;
              result.elements.push({
                data: {
                  id: `${source}~${nodeId}`,
                  source,
                  target: nodeId,
                },
              });
              // Get trained model for analytics job and create model node
              ({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements(
                jobs[i].id,
                jobs[i].create_time!
              ));
              if (isAnalyticsMapNodeElement(modelElement)) {
                modelElements.push(modelElement);
                result.details[modelElement.data.id] = modelDetails;
              }
              if (isAnalyticsMapEdgeElement(edgeElement)) {
                modelElements.push(edgeElement);
              }
            }
          }
        }
      }
      // Include model and index pattern nodes in result elements now that all other nodes have been created
      result.elements.push(...modelElements, ...indexPatternElements);
      return result;
    } catch (error) {
      result.error = error.message || 'An error occurred fetching map';
      return result;
    }
  }