<commit_before>import { useState, useEffect, useMemo } from 'react';
import Datasource from '../../datasource';
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
import { hasOption, toOption } from '../../utils/common';
import {
  setMetricName,
  setMetricNamespace,
  setResourceGroup,
  setResourceName,
  setResourceType,
  setSubscriptionID,
} from './setQueryValue';

export interface MetricMetadata {
  aggOptions: AzureMonitorOption[];
  timeGrains: AzureMonitorOption[];
  dimensions: AzureMonitorOption[];
  isLoading: boolean;

  // These two properties are only used within the hook, and not elsewhere
  supportedAggTypes: string[];
  primaryAggType: string | undefined;
}

type SetErrorFn = (source: string, error: AzureMonitorErrorish | undefined) => void;
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;

type DataHook = (
  query: AzureMonitorQuery,
  datasource: Datasource,
  onChange: OnChangeFn,
  setError: SetErrorFn
) => AzureMonitorOption[];

export function useAsyncState<T>(asyncFn: () => Promise<T>, setError: Function, dependencies: unknown[]) {
  // Use the lazy initial state functionality of useState to assign a random ID to the API call
  // to track where errors come from. See useLastError.
  const [errorSource] = useState(() => Math.random());
  const [value, setValue] = useState<T>();

  const finalValue = useMemo(() => value ?? [], [value]);

  useEffect(() => {
    asyncFn()
      .then((results) => {
        setValue(results);
        setError(errorSource, undefined);
      })
      .catch((err) => {
        setError(errorSource, err);
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, dependencies);

  return finalValue;
}

export const useSubscriptions: DataHook = (query, datasource, onChange, setError) => {
  const { subscription } = query;
  const defaultSubscription = datasource.azureMonitorDatasource.defaultSubscriptionId;

  const subscriptionOptions = useAsyncState(
    async () => {
      const results = await datasource.azureMonitorDatasource.getSubscriptions();
      return results.map((v) => ({ label: v.text, value: v.value, description: v.value }));
    },
    setError,
    []
  );

  useEffect(() => {
    if (!subscription && defaultSubscription && hasOption(subscriptionOptions, defaultSubscription)) {
      onChange(setSubscriptionID(query, defaultSubscription));
    } else if ((!subscription && subscriptionOptions.length) || subscriptionOptions.length === 1) {
      onChange(setSubscriptionID(query, subscriptionOptions[0].value));
    }
  }, [subscriptionOptions, query, subscription, defaultSubscription, onChange]);

  return subscriptionOptions;
};

export const useResourceGroups: DataHook = (query, datasource, onChange, setError) => {
  const { subscription } = query;
  const { resourceGroup } = query.azureMonitor ?? {};

  return useAsyncState(
    async () => {
      if (!subscription) {
        return;
      }

      const results = await datasource.getResourceGroups(subscription);
      const options = results.map(toOption);

      if (resourceGroup && !hasOption(options, resourceGroup)) {
        onChange(setResourceGroup(query, undefined));
      }

      return options;
    },<commit_msg>AzureMonitor: Fix crash from infinite render loop (#37924)<commit_after>import { useState, useEffect, useMemo } from 'react';
import Datasource from '../../datasource';
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
import { hasOption, toOption } from '../../utils/common';
import {
  setMetricName,
  setMetricNamespace,
  setResourceGroup,
  setResourceName,
  setResourceType,
  setSubscriptionID,
} from './setQueryValue';

export interface MetricMetadata {
  aggOptions: AzureMonitorOption[];
  timeGrains: AzureMonitorOption[];
  dimensions: AzureMonitorOption[];
  isLoading: boolean;

  // These two properties are only used within the hook, and not elsewhere
  supportedAggTypes: string[];
  primaryAggType: string | undefined;
}

type SetErrorFn = (source: string, error: AzureMonitorErrorish | undefined) => void;
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;

type DataHook = (
  query: AzureMonitorQuery,
  datasource: Datasource,
  onChange: OnChangeFn,
  setError: SetErrorFn
) => AzureMonitorOption[];

export function useAsyncState<T>(asyncFn: () => Promise<T>, setError: Function, dependencies: unknown[]) {
  // Use the lazy initial state functionality of useState to assign a random ID to the API call
  // to track where errors come from. See useLastError.
  const [errorSource] = useState(() => Math.random());
  const [value, setValue] = useState<T>();

  const finalValue = useMemo(() => value ?? [], [value]);

  useEffect(() => {
    asyncFn()
      .then((results) => {
        setValue(results);
        setError(errorSource, undefined);
      })
      .catch((err) => {
        setError(errorSource, err);
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, dependencies);

  return finalValue;
}

export const useSubscriptions: DataHook = (query, datasource, onChange, setError) => {
  const { subscription } = query;
  const defaultSubscription = datasource.azureMonitorDatasource.defaultSubscriptionId;

  const subscriptionOptions = useAsyncState(
    async () => {
      const results = await datasource.azureMonitorDatasource.getSubscriptions();
      return results.map((v) => ({ label: v.text, value: v.value, description: v.value }));
    },
    setError,
    []
  );

  useEffect(() => {
    // Return early if subscriptions havent loaded, or if the query already has a subscription
    if (!subscriptionOptions.length || (subscription && hasOption(subscriptionOptions, subscription))) {
      return;
    }

    const defaultSub = defaultSubscription || subscriptionOptions[0].value;

    if (!subscription && defaultSub && hasOption(subscriptionOptions, defaultSub)) {
      onChange(setSubscriptionID(query, defaultSub));
    }
  }, [subscriptionOptions, query, subscription, defaultSubscription, onChange]);

  return subscriptionOptions;
};

export const useResourceGroups: DataHook = (query, datasource, onChange, setError) => {
  const { subscription } = query;
  const { resourceGroup } = query.azureMonitor ?? {};

  return useAsyncState(
    async () => {
      if (!subscription) {
        return;
      }

      const results = await datasource.getResourceGroups(subscription);
      const options = results.map(toOption);

      if (resourceGroup && !hasOption(options, resourceGroup)) {
        onChange(setResourceGroup(query, undefined));
      }

      return options;
    },
{'fraction_non_alphanumeric': 0.12451303566077315, 'fraction_numerical': 0.001198681450404555, 'mean_word_length': 4.037735849056604, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6993131', 'n_tokens_mistral': 1816, 'n_tokens_neox': 1837, 'n_words': 485}