-
Aleksandr Lesnenko authoredAleksandr Lesnenko authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ImpersonationModal.tsx 4.04 KiB
import { useCallback } from "react";
import { withRouter } from "react-router";
import { push } from "react-router-redux";
import { useAsyncFn, useMount } from "react-use";
import { useSelector } from "react-redux";
import { useDispatch } from "metabase/lib/redux";
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper/LoadingAndErrorWrapper";
import {
AdvancedPermissionsStoreState,
ImpersonationModalParams,
ImpersonationParams,
} from "metabase-enterprise/advanced_permissions/types";
import { getParentPath } from "metabase/hoc/ModalRoute";
import { useDatabaseQuery } from "metabase/common/hooks";
import { getImpersonation } from "metabase-enterprise/advanced_permissions/selectors";
import { updateImpersonation } from "metabase-enterprise/advanced_permissions/reducer";
import { updateDataPermission } from "metabase/admin/permissions/permissions";
import { ImpersonationApi } from "metabase-enterprise/advanced_permissions/services";
import { Impersonation } from "metabase-types/api";
import { fetchUserAttributes } from "metabase-enterprise/shared/reducer";
import { getUserAttributes } from "metabase-enterprise/shared/selectors";
import { getImpersonatedDatabaseId } from "metabase-enterprise/advanced_permissions/utils";
import { ImpersonationModalView } from "./ImpersonationModalView";
interface ImpersonationModalProps {
params: ImpersonationModalParams;
route: {
path: string;
};
}
const parseParams = (params: ImpersonationModalParams): ImpersonationParams => {
const groupId = parseInt(params.groupId);
const databaseId = getImpersonatedDatabaseId(params);
return {
groupId,
databaseId,
};
};
const _ImpersonationModal = ({ route, params }: ImpersonationModalProps) => {
const [
{
loading: isImpersonationLoading,
value: impersonation,
error: impersonationError,
},
fetchImpersonation,
] = useAsyncFn(
async (
groupId: number,
databaseId: number,
): Promise<Impersonation | undefined> =>
ImpersonationApi.get({
db_id: databaseId,
group_id: groupId,
}),
[],
);
const { groupId, databaseId } = parseParams(params);
const {
data: database,
isLoading: isDatabaseLoading,
error,
} = useDatabaseQuery({
id: databaseId,
});
const attributes = useSelector(getUserAttributes);
const draftImpersonation = useSelector<
AdvancedPermissionsStoreState,
Impersonation | undefined
>(getImpersonation(databaseId, groupId));
const selectedAttribute =
draftImpersonation?.attribute ?? impersonation?.attribute;
const dispatch = useDispatch();
const close = useCallback(() => {
dispatch(push(getParentPath(route, location)));
}, [dispatch, route]);
const handleSave = useCallback(
attribute => {
dispatch(
updateDataPermission({
groupId,
permission: { type: "access", permission: "data" },
value: "impersonated",
entityId: { databaseId },
}),
);
if (attribute !== selectedAttribute) {
dispatch(
updateImpersonation({
attribute,
db_id: databaseId,
group_id: groupId,
}),
);
}
close();
},
[close, databaseId, dispatch, groupId, selectedAttribute],
);
const handleCancel = useCallback(() => {
dispatch(push(getParentPath(route, location)));
}, [dispatch, route]);
useMount(() => {
dispatch(fetchUserAttributes());
if (!draftImpersonation) {
fetchImpersonation(groupId, databaseId);
}
});
const isLoading =
isDatabaseLoading || isImpersonationLoading || !attributes || !database;
if (isLoading) {
return (
<LoadingAndErrorWrapper
loading={isLoading}
error={error ?? impersonationError}
/>
);
}
return (
<ImpersonationModalView
selectedAttribute={selectedAttribute}
attributes={attributes}
database={database}
onSave={handleSave}
onCancel={handleCancel}
/>
);
};
export const ImpersonationModal = withRouter(_ImpersonationModal);