sshPasswordNeeded: getSSHPasswordsNeeded()

in superset-frontend/src/views/CRUD/hooks.ts [539:781]


                sshPasswordNeeded: getSSHPasswordsNeeded(error.errors),
                sshPrivateKeyNeeded: getSSHPrivateKeysNeeded(error.errors),
                sshPrivateKeyPasswordNeeded: getSSHPrivateKeyPasswordsNeeded(
                  error.errors,
                ),
                alreadyExists: getAlreadyExists(error.errors),
              });
            }
            return false;
          }),
        )
        .finally(() => {
          updateState({ loading: false });
        });
    },
    [],
  );

  return { state, importResource };
}

type FavoriteStatusResponse = {
  result: Array<{
    id: string;
    value: boolean;
  }>;
};

const favoriteApis = {
  chart: makeApi<Array<string | number>, FavoriteStatusResponse>({
    requestType: 'rison',
    method: 'GET',
    endpoint: '/api/v1/chart/favorite_status/',
  }),
  dashboard: makeApi<Array<string | number>, FavoriteStatusResponse>({
    requestType: 'rison',
    method: 'GET',
    endpoint: '/api/v1/dashboard/favorite_status/',
  }),
  tag: makeApi<Array<string | number>, FavoriteStatusResponse>({
    requestType: 'rison',
    method: 'GET',
    endpoint: '/api/v1/tag/favorite_status/',
  }),
};

export function useFavoriteStatus(
  type: 'chart' | 'dashboard' | 'tag',
  ids: Array<string | number>,
  handleErrorMsg: (message: string) => void,
) {
  const [favoriteStatus, setFavoriteStatus] = useState<FavoriteStatus>({});

  const updateFavoriteStatus = (update: FavoriteStatus) =>
    setFavoriteStatus(currentState => ({ ...currentState, ...update }));

  useEffect(() => {
    if (!ids.length) {
      return;
    }
    favoriteApis[type](ids).then(
      ({ result }) => {
        const update = result.reduce<Record<string, boolean>>(
          (acc, element) => {
            acc[element.id] = element.value;
            return acc;
          },
          {},
        );
        updateFavoriteStatus(update);
      },
      createErrorHandler(errMsg =>
        handleErrorMsg(
          t('There was an error fetching the favorite status: %s', errMsg),
        ),
      ),
    );
  }, [ids, type, handleErrorMsg]);

  const saveFaveStar = useCallback(
    (id: number, isStarred: boolean) => {
      const endpoint = `/api/v1/${type}/${id}/favorites/`;
      const apiCall = isStarred
        ? SupersetClient.delete({
            endpoint,
          })
        : SupersetClient.post({ endpoint });

      apiCall.then(
        () => {
          updateFavoriteStatus({
            [id]: !isStarred,
          });
        },
        createErrorHandler(errMsg =>
          handleErrorMsg(
            t('There was an error saving the favorite status: %s', errMsg),
          ),
        ),
      );
    },
    [type],
  );

  return [saveFaveStar, favoriteStatus] as const;
}

export const useChartEditModal = (
  setCharts: (charts: Array<Chart>) => void,
  charts: Array<Chart>,
) => {
  const [sliceCurrentlyEditing, setSliceCurrentlyEditing] =
    useState<Slice | null>(null);

  function openChartEditModal(chart: Chart) {
    setSliceCurrentlyEditing({
      slice_id: chart.id,
      slice_name: chart.slice_name,
      description: chart.description,
      cache_timeout: chart.cache_timeout,
      certified_by: chart.certified_by,
      certification_details: chart.certification_details,
      is_managed_externally: chart.is_managed_externally,
    });
  }

  function closeChartEditModal() {
    setSliceCurrentlyEditing(null);
  }

  function handleChartUpdated(edits: Chart) {
    // update the chart in our state with the edited info
    const newCharts = charts.map((chart: Chart) =>
      chart.id === edits.id ? { ...chart, ...edits } : chart,
    );
    setCharts(newCharts);
  }

  return {
    sliceCurrentlyEditing,
    handleChartUpdated,
    openChartEditModal,
    closeChartEditModal,
  };
};

export const copyQueryLink = (
  id: number,
  addDangerToast: (arg0: string) => void,
  addSuccessToast: (arg0: string) => void,
) => {
  copyTextToClipboard(() =>
    Promise.resolve(
      `${window.location.origin}${ensureAppRoot(`/sqllab?savedQueryId=${id}`)}`,
    ),
  )
    .then(() => {
      addSuccessToast(t('Link Copied!'));
    })
    .catch(() => {
      addDangerToast(t('Sorry, your browser does not support copying.'));
    });
};

export const getDatabaseImages = () => SupersetText.DB_IMAGES;

export const getConnectionAlert = () => SupersetText.DB_CONNECTION_ALERTS;
export const getDatabaseDocumentationLinks = () =>
  SupersetText.DB_CONNECTION_DOC_LINKS;

export const testDatabaseConnection = (
  connection: Partial<DatabaseObject>,
  handleErrorMsg: (errorMsg: string) => void,
  addSuccessToast: (arg0: string) => void,
) => {
  SupersetClient.post({
    endpoint: 'api/v1/database/test_connection/',
    body: JSON.stringify(connection),
    headers: { 'Content-Type': 'application/json' },
  }).then(
    () => {
      addSuccessToast(t('Connection looks good!'));
    },
    createErrorHandler((errMsg: Record<string, string[] | string> | string) => {
      handleErrorMsg(t('ERROR: %s', parsedErrorMessage(errMsg)));
    }),
  );
};

export function useAvailableDatabases() {
  const [availableDbs, setAvailableDbs] = useState<JsonObject | null>(null);

  const getAvailable = useCallback(() => {
    SupersetClient.get({
      endpoint: `/api/v1/database/available/`,
    }).then(({ json }) => {
      setAvailableDbs(json);
    });
  }, [setAvailableDbs]);

  return [availableDbs, getAvailable] as const;
}

const transformDB = (db: Partial<DatabaseObject> | null) => {
  if (db && Array.isArray(db?.catalog)) {
    return {
      ...db,
      catalog: Object.assign(
        {},
        ...db.catalog.map((x: { name: string; value: string }) => ({
          [x.name]: x.value,
        })),
      ),
    };
  }
  return db;
};

export function useDatabaseValidation() {
  const [validationErrors, setValidationErrors] = useState<JsonObject | null>(
    null,
  );
  const getValidation = useCallback(
    (database: Partial<DatabaseObject> | null, onCreate = false) => {
      if (database?.parameters?.ssh) {
        // TODO: /validate_parameters/ and related utils should support ssh tunnel
        setValidationErrors(null);
        return [];
      }

      return (
        SupersetClient.post({
          endpoint: '/api/v1/database/validate_parameters/',
          body: JSON.stringify(transformDB(database)),
          headers: { 'Content-Type': 'application/json' },
        })
          .then(() => {
            setValidationErrors(null);
          })
          // eslint-disable-next-line consistent-return
          .catch(e => {
            if (typeof e.json === 'function') {
              return e.json().then(({ errors = [] }: JsonObject) => {