mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-13 09:48:19 +00:00
This commit is contained in:
parent
819188cfc1
commit
c27657ea5e
@ -65,14 +65,9 @@ describe('Bots Page should work properly', () => {
|
||||
cy.get('[data-testid="displayName"]').should('exist').type(botName);
|
||||
//Enter description
|
||||
cy.get(descriptionBox).type(description);
|
||||
//Generate Password
|
||||
interceptURL('GET', ' /api/v1/users/generateRandomPwd', 'generatePassword');
|
||||
cy.get('[data-testid="password-generator"]').should('be.visible').click();
|
||||
verifyResponseStatusCode('@generatePassword', 200);
|
||||
cy.wait(1000);
|
||||
//Click on save button
|
||||
cy.wait(1000);
|
||||
interceptURL('POST', '/api/v1/bots', 'createBot');
|
||||
interceptURL('PUT', '/api/v1/bots', 'createBot');
|
||||
cy.get('[data-testid="save-user"]')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
@ -97,9 +92,8 @@ describe('Bots Page should work properly', () => {
|
||||
.type(updatedBotName);
|
||||
//Save the updated display name
|
||||
|
||||
interceptURL('GET', '/api/v1/users/auth-mechanism/*', 'getBotDetails');
|
||||
cy.get('[data-testid="save-displayName"]').should('be.visible').click();
|
||||
verifyResponseStatusCode('@getBotDetails', 200);
|
||||
|
||||
//Verify the display name is updated on bot details page
|
||||
cy.get('[data-testid="container"]').should('contain', updatedBotName);
|
||||
cy.wait(1000);
|
||||
|
@ -63,3 +63,12 @@ export const deleteBot = async (id: string, hardDelete?: boolean) => {
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const createBotWithPut = async (data: CreateBot) => {
|
||||
const response = await axiosClient.put<CreateBot, AxiosResponse<Bot>>(
|
||||
BASE_URL,
|
||||
data
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
AuthenticationMechanism,
|
||||
CreateUser,
|
||||
} from '../generated/api/teams/createUser';
|
||||
import { Bot } from '../generated/entity/bot';
|
||||
import { JwtAuth } from '../generated/entity/teams/authN/jwtAuth';
|
||||
import { User } from '../generated/entity/teams/user';
|
||||
import { EntityReference } from '../generated/type/entityReference';
|
||||
@ -196,3 +197,34 @@ export const getAuthMechanismForBotUser = async (botId: string) => {
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getBotByName = async (name: string, arrQueryFields?: string) => {
|
||||
const url = getURLWithQueryFields(`/bots/name/${name}`, arrQueryFields);
|
||||
|
||||
const response = await APIClient.get<Bot>(url);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateBotDetail = async (id: string, data: Operation[]) => {
|
||||
const configOptions = {
|
||||
headers: { 'Content-type': 'application/json-patch+json' },
|
||||
};
|
||||
|
||||
const response = await APIClient.patch<Operation[], AxiosResponse<Bot>>(
|
||||
`/bots/${id}`,
|
||||
data,
|
||||
configOptions
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const createUserWithPut = async (userDetails: CreateUser) => {
|
||||
const response = await APIClient.put<CreateUser, AxiosResponse<User>>(
|
||||
`/users`,
|
||||
userDetails
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
@ -15,13 +15,17 @@ import { Button, Divider, Input, Space, Typography } from 'antd';
|
||||
import { capitalize } from 'lodash';
|
||||
import React, { FC } from 'react';
|
||||
import { AuthType } from '../../generated/api/teams/createUser';
|
||||
import { AuthenticationMechanism } from '../../generated/entity/teams/user';
|
||||
import {
|
||||
AuthenticationMechanism,
|
||||
User,
|
||||
} from '../../generated/entity/teams/user';
|
||||
import { getTokenExpiry } from '../../utils/BotsUtils';
|
||||
import SVGIcons from '../../utils/SvgUtils';
|
||||
import CopyToClipboardButton from '../buttons/CopyToClipboardButton/CopyToClipboardButton';
|
||||
import './AuthMechanism.less';
|
||||
|
||||
interface Props {
|
||||
botUser: User;
|
||||
authenticationMechanism: AuthenticationMechanism;
|
||||
hasPermission: boolean;
|
||||
onEdit: () => void;
|
||||
@ -33,6 +37,7 @@ const AuthMechanism: FC<Props> = ({
|
||||
hasPermission,
|
||||
onEdit,
|
||||
onTokenRevoke,
|
||||
botUser,
|
||||
}: Props) => {
|
||||
if (authenticationMechanism.authType === AuthType.Jwt) {
|
||||
const JWTToken = authenticationMechanism.config?.JWTToken;
|
||||
@ -137,6 +142,18 @@ const AuthMechanism: FC<Props> = ({
|
||||
<Divider style={{ margin: '8px 0px' }} />
|
||||
|
||||
<Space className="w-full" direction="vertical">
|
||||
<>
|
||||
<Typography.Text>Account Email</Typography.Text>
|
||||
<Space className="w-full tw-justify-between ant-space-authMechanism">
|
||||
<Input
|
||||
contentEditable={false}
|
||||
data-testid="botUser-email"
|
||||
value={botUser.email}
|
||||
/>
|
||||
<CopyToClipboardButton copyText={botUser.email} />
|
||||
</Space>
|
||||
</>
|
||||
|
||||
{authConfig?.secretKey && (
|
||||
<>
|
||||
<Typography.Text>SecretKey</Typography.Text>
|
||||
|
@ -11,35 +11,51 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Form, Input, Select, Space } from 'antd';
|
||||
import { Button, Form, Input, Modal, Select, Space, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { checkEmailInUse } from '../../axiosAPIs/auth-API';
|
||||
import { createBotWithPut } from '../../axiosAPIs/botsAPI';
|
||||
import { createUserWithPut, getUserByName } from '../../axiosAPIs/userAPI';
|
||||
import { BOT_ACCOUNT_EMAIL_CHANGE_CONFIRMATION } from '../../constants/HelperTextUtil';
|
||||
import { validEmailRegEx } from '../../constants/regex.constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Bot } from '../../generated/entity/bot';
|
||||
import { SsoServiceType } from '../../generated/entity/teams/authN/ssoAuth';
|
||||
import {
|
||||
AuthenticationMechanism,
|
||||
AuthType,
|
||||
JWTTokenExpiry,
|
||||
User,
|
||||
} from '../../generated/entity/teams/user';
|
||||
import { Auth0SSOClientConfig } from '../../generated/security/client/auth0SSOClientConfig';
|
||||
import { AzureSSOClientConfig } from '../../generated/security/client/azureSSOClientConfig';
|
||||
import { CustomOidcSSOClientConfig } from '../../generated/security/client/customOidcSSOClientConfig';
|
||||
import { GoogleSSOClientConfig } from '../../generated/security/client/googleSSOClientConfig';
|
||||
import { OktaSSOClientConfig } from '../../generated/security/client/oktaSSOClientConfig';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { getNameFromEmail } from '../../utils/AuthProvider.util';
|
||||
import {
|
||||
getAuthMechanismFormInitialValues,
|
||||
getAuthMechanismTypeOptions,
|
||||
getJWTTokenExpiryOptions,
|
||||
} from '../../utils/BotsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { SSOClientConfig } from '../CreateUser/CreateUser.interface';
|
||||
import Loader from '../Loader/Loader';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
interface Props {
|
||||
botUser: User;
|
||||
botData: Bot;
|
||||
isUpdating: boolean;
|
||||
authenticationMechanism: AuthenticationMechanism;
|
||||
onSave: (updatedAuthMechanism: AuthenticationMechanism) => void;
|
||||
onCancel: () => void;
|
||||
onEmailChange: () => void;
|
||||
}
|
||||
|
||||
const AuthMechanismForm: FC<Props> = ({
|
||||
@ -47,6 +63,9 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
onSave,
|
||||
onCancel,
|
||||
authenticationMechanism,
|
||||
botUser,
|
||||
botData,
|
||||
onEmailChange,
|
||||
}) => {
|
||||
const { authConfig } = useAuthContext();
|
||||
|
||||
@ -62,6 +81,11 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
({} as SSOClientConfig)
|
||||
);
|
||||
|
||||
const [accountEmail, setAccountEmail] = useState<string>(botUser.email);
|
||||
|
||||
const [isConfirmationModalOpen, setIsConfirmationModalOpen] =
|
||||
useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
const authType = authenticationMechanism.authType;
|
||||
const authConfig = authenticationMechanism.config?.authConfig;
|
||||
@ -114,6 +138,10 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
email: value,
|
||||
}));
|
||||
|
||||
break;
|
||||
case 'email':
|
||||
setAccountEmail(value);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -122,22 +150,80 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const updatedAuthMechanism: AuthenticationMechanism = {
|
||||
authType: authMechanism,
|
||||
config:
|
||||
authMechanism === AuthType.Jwt
|
||||
? {
|
||||
JWTTokenExpiry: tokenExpiry,
|
||||
}
|
||||
: {
|
||||
ssoServiceType: authConfig?.provider as SsoServiceType,
|
||||
authConfig: {
|
||||
...ssoClientConfig,
|
||||
if (accountEmail !== botUser.email) {
|
||||
setIsConfirmationModalOpen(true);
|
||||
} else {
|
||||
const updatedAuthMechanism: AuthenticationMechanism = {
|
||||
authType: authMechanism,
|
||||
config:
|
||||
authMechanism === AuthType.Jwt
|
||||
? {
|
||||
JWTTokenExpiry: tokenExpiry,
|
||||
}
|
||||
: {
|
||||
ssoServiceType: authConfig?.provider as SsoServiceType,
|
||||
authConfig: {
|
||||
...ssoClientConfig,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
onSave(updatedAuthMechanism);
|
||||
onSave(updatedAuthMechanism);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBotUpdate = async (response: User) => {
|
||||
try {
|
||||
await createBotWithPut({
|
||||
name: botData.name,
|
||||
description: botData.description,
|
||||
displayName: botData.displayName,
|
||||
botUser: { id: response.id, type: EntityType.USER },
|
||||
});
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
onEmailChange();
|
||||
setIsConfirmationModalOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAccountEmailChange = async () => {
|
||||
try {
|
||||
const isUserExists = await checkEmailInUse(accountEmail);
|
||||
if (isUserExists) {
|
||||
const userResponse = await getUserByName(
|
||||
getNameFromEmail(accountEmail)
|
||||
);
|
||||
handleBotUpdate(userResponse);
|
||||
} else {
|
||||
const userResponse = await createUserWithPut({
|
||||
email: accountEmail,
|
||||
name: getNameFromEmail(accountEmail),
|
||||
botName: botData.name,
|
||||
isBot: true,
|
||||
authenticationMechanism: {
|
||||
authType: authMechanism,
|
||||
config:
|
||||
authMechanism === AuthType.Jwt
|
||||
? {
|
||||
JWTTokenExpiry: tokenExpiry,
|
||||
}
|
||||
: {
|
||||
ssoServiceType: authConfig?.provider as SsoServiceType,
|
||||
authConfig: {
|
||||
...ssoClientConfig,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
handleBotUpdate(userResponse);
|
||||
}
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
setIsConfirmationModalOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getSSOConfig = () => {
|
||||
@ -464,47 +550,24 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
id="update-auth-mechanism-form"
|
||||
layout="vertical"
|
||||
onFinish={handleSave}>
|
||||
<Form.Item
|
||||
label="Auth Mechanism"
|
||||
name="auth-mechanism"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: () => {
|
||||
if (!authMechanism) {
|
||||
return Promise.reject('Auth Mechanism is required');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<Select
|
||||
className="w-full"
|
||||
data-testid="auth-mechanism"
|
||||
defaultValue={authMechanism}
|
||||
placeholder="Select Auth Mechanism"
|
||||
onChange={(value) => setAuthMechanism(value)}>
|
||||
{getAuthMechanismTypeOptions(authConfig).map((option) => (
|
||||
<Option key={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{authMechanism === AuthType.Jwt && (
|
||||
<>
|
||||
<Form
|
||||
id="update-auth-mechanism-form"
|
||||
initialValues={getAuthMechanismFormInitialValues(
|
||||
authenticationMechanism,
|
||||
botUser
|
||||
)}
|
||||
layout="vertical"
|
||||
onFinish={handleSave}>
|
||||
<Form.Item
|
||||
label="Token Expiration"
|
||||
name="token-expiration"
|
||||
label="Auth Mechanism"
|
||||
name="auth-mechanism"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: () => {
|
||||
if (!tokenExpiry) {
|
||||
return Promise.reject('Token Expiration is required');
|
||||
if (!authMechanism) {
|
||||
return Promise.reject('Auth Mechanism is required');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
@ -513,32 +576,98 @@ const AuthMechanismForm: FC<Props> = ({
|
||||
]}>
|
||||
<Select
|
||||
className="w-full"
|
||||
data-testid="token-expiry"
|
||||
defaultValue={tokenExpiry}
|
||||
placeholder="Select Token Expiration"
|
||||
onChange={(value) => setTokenExpiry(value)}>
|
||||
{getJWTTokenExpiryOptions().map((option) => (
|
||||
data-testid="auth-mechanism"
|
||||
defaultValue={authMechanism}
|
||||
placeholder="Select Auth Mechanism"
|
||||
onChange={(value) => setAuthMechanism(value)}>
|
||||
{getAuthMechanismTypeOptions(authConfig).map((option) => (
|
||||
<Option key={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
{authMechanism === AuthType.Sso && <>{getSSOConfig()}</>}
|
||||
<Space className="w-full tw-justify-end" size={4}>
|
||||
{!isEmpty(authenticationMechanism) && (
|
||||
<Button data-testid="cancel-edit" type="link" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
{authMechanism === AuthType.Jwt && (
|
||||
<Form.Item
|
||||
label="Token Expiration"
|
||||
name="token-expiration"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: () => {
|
||||
if (!tokenExpiry) {
|
||||
return Promise.reject('Token Expiration is required');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<Select
|
||||
className="w-full"
|
||||
data-testid="token-expiry"
|
||||
defaultValue={tokenExpiry}
|
||||
placeholder="Select Token Expiration"
|
||||
onChange={(value) => setTokenExpiry(value)}>
|
||||
{getJWTTokenExpiryOptions().map((option) => (
|
||||
<Option key={option.value}>{option.label}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Button
|
||||
data-testid="save-edit"
|
||||
form="update-auth-mechanism-form"
|
||||
htmlType="submit"
|
||||
type="primary">
|
||||
{isUpdating ? <Loader size="small" /> : 'Save'}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
{authMechanism === AuthType.Sso && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Email"
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
pattern: validEmailRegEx,
|
||||
required: true,
|
||||
type: 'email',
|
||||
message: jsonData['form-error-messages']['invalid-email'],
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
data-testid="email"
|
||||
name="email"
|
||||
placeholder="email"
|
||||
value={accountEmail}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
{getSSOConfig()}
|
||||
</>
|
||||
)}
|
||||
<Space className="w-full tw-justify-end" size={4}>
|
||||
{!isEmpty(authenticationMechanism) && (
|
||||
<Button data-testid="cancel-edit" type="link" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
data-testid="save-edit"
|
||||
form="update-auth-mechanism-form"
|
||||
htmlType="submit"
|
||||
type="primary">
|
||||
{isUpdating ? <Loader size="small" /> : 'Save'}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
{isConfirmationModalOpen && (
|
||||
<Modal
|
||||
centered
|
||||
destroyOnClose
|
||||
okText="Confirm"
|
||||
title="Are you sure?"
|
||||
visible={isConfirmationModalOpen}
|
||||
onCancel={() => setIsConfirmationModalOpen(false)}
|
||||
onOk={handleAccountEmailChange}>
|
||||
<Typography.Text>
|
||||
{BOT_ACCOUNT_EMAIL_CHANGE_CONFIRMATION} for {botData.name} bot.
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -22,20 +22,22 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { createBotWithPut } from '../../axiosAPIs/botsAPI';
|
||||
import {
|
||||
createUserWithPut,
|
||||
getAuthMechanismForBotUser,
|
||||
updateUser,
|
||||
} from '../../axiosAPIs/userAPI';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
GlobalSettingsMenuCategory,
|
||||
} from '../../constants/globalSettings.constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Bot } from '../../generated/entity/bot';
|
||||
import {
|
||||
AuthenticationMechanism,
|
||||
AuthType,
|
||||
User,
|
||||
} from '../../generated/entity/teams/user';
|
||||
import { EntityReference } from '../../generated/type/entityReference';
|
||||
import { getEntityName } from '../../utils/CommonUtils';
|
||||
import { getSettingPath } from '../../utils/RouterUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
@ -51,19 +53,23 @@ import AuthMechanism from './AuthMechanism';
|
||||
import AuthMechanismForm from './AuthMechanismForm';
|
||||
|
||||
interface BotsDetailProp extends HTMLAttributes<HTMLDivElement> {
|
||||
botsData: User;
|
||||
botUserData: User;
|
||||
botData: Bot;
|
||||
botPermission: OperationPermission;
|
||||
updateBotsDetails: (data: UserDetails) => Promise<void>;
|
||||
revokeTokenHandler: () => void;
|
||||
onEmailChange: () => void;
|
||||
}
|
||||
|
||||
const BotDetails: FC<BotsDetailProp> = ({
|
||||
botsData,
|
||||
botData,
|
||||
botUserData,
|
||||
updateBotsDetails,
|
||||
revokeTokenHandler,
|
||||
botPermission,
|
||||
onEmailChange,
|
||||
}) => {
|
||||
const [displayName, setDisplayName] = useState(botsData.displayName);
|
||||
const [displayName, setDisplayName] = useState(botData.displayName);
|
||||
const [isDisplayNameEdit, setIsDisplayNameEdit] = useState(false);
|
||||
const [isDescriptionEdit, setIsDescriptionEdit] = useState(false);
|
||||
const [isRevokingToken, setIsRevokingToken] = useState<boolean>(false);
|
||||
@ -92,7 +98,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
|
||||
const fetchAuthMechanismForBot = async () => {
|
||||
try {
|
||||
const response = await getAuthMechanismForBotUser(botsData.id);
|
||||
const response = await getAuthMechanismForBotUser(botUserData.id);
|
||||
setAuthenticationMechanism(response);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
@ -106,7 +112,6 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
try {
|
||||
const {
|
||||
isAdmin,
|
||||
teams,
|
||||
timezone,
|
||||
name,
|
||||
description,
|
||||
@ -114,11 +119,9 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
profile,
|
||||
email,
|
||||
isBot,
|
||||
roles,
|
||||
} = botsData;
|
||||
const response = await updateUser({
|
||||
} = botUserData;
|
||||
const response = await createUserWithPut({
|
||||
isAdmin,
|
||||
teams,
|
||||
timezone,
|
||||
name,
|
||||
description,
|
||||
@ -126,9 +129,8 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
profile,
|
||||
email,
|
||||
isBot,
|
||||
roles,
|
||||
authenticationMechanism: {
|
||||
...botsData.authenticationMechanism,
|
||||
...botUserData.authenticationMechanism,
|
||||
authType: updatedAuthMechanism.authType,
|
||||
config:
|
||||
updatedAuthMechanism.authType === AuthType.Jwt
|
||||
@ -140,8 +142,15 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
authConfig: updatedAuthMechanism.config?.authConfig,
|
||||
},
|
||||
},
|
||||
} as User);
|
||||
if (response.data) {
|
||||
botName: botData.name,
|
||||
});
|
||||
if (response) {
|
||||
await createBotWithPut({
|
||||
name: botData.name,
|
||||
description: botData.description,
|
||||
displayName: botData.displayName,
|
||||
botUser: { id: response.id, type: EntityType.USER },
|
||||
});
|
||||
fetchAuthMechanismForBot();
|
||||
}
|
||||
} catch (error) {
|
||||
@ -159,7 +168,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
};
|
||||
|
||||
const handleDisplayNameChange = () => {
|
||||
if (displayName !== botsData.displayName) {
|
||||
if (displayName !== botData.displayName) {
|
||||
updateBotsDetails({ displayName: displayName || '' });
|
||||
}
|
||||
setIsDisplayNameEdit(false);
|
||||
@ -243,8 +252,8 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
return (
|
||||
<div className="tw--ml-5">
|
||||
<Description
|
||||
description={botsData.description || ''}
|
||||
entityName={getEntityName(botsData as unknown as EntityReference)}
|
||||
description={botData.description || ''}
|
||||
entityName={getEntityName(botData)}
|
||||
hasEditAccess={descriptionPermission || editAllPermission}
|
||||
isEdit={isDescriptionEdit}
|
||||
onCancel={() => setIsDescriptionEdit(false)}
|
||||
@ -303,10 +312,10 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (botsData.id) {
|
||||
if (botUserData.id) {
|
||||
fetchAuthMechanismForBot();
|
||||
}
|
||||
}, [botsData]);
|
||||
}, [botUserData]);
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
@ -322,7 +331,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
GlobalSettingOptions.BOTS
|
||||
),
|
||||
},
|
||||
{ name: botsData.name || '', url: '', activeTitle: true },
|
||||
{ name: botData.name || '', url: '', activeTitle: true },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
@ -339,13 +348,17 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
{isAuthMechanismEdit ? (
|
||||
<AuthMechanismForm
|
||||
authenticationMechanism={authenticationMechanism}
|
||||
botData={botData}
|
||||
botUser={botUserData}
|
||||
isUpdating={isUpdating}
|
||||
onCancel={() => setIsAuthMechanismEdit(false)}
|
||||
onEmailChange={onEmailChange}
|
||||
onSave={handleAuthMechanismUpdate}
|
||||
/>
|
||||
) : (
|
||||
<AuthMechanism
|
||||
authenticationMechanism={authenticationMechanism}
|
||||
botUser={botUserData}
|
||||
hasPermission={editAllPermission}
|
||||
onEdit={handleAuthMechanismEdit}
|
||||
onTokenRevoke={() => setIsRevokingToken(true)}
|
||||
@ -355,8 +368,11 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
) : (
|
||||
<AuthMechanismForm
|
||||
authenticationMechanism={{} as AuthenticationMechanism}
|
||||
botData={botData}
|
||||
botUser={botUserData}
|
||||
isUpdating={isUpdating}
|
||||
onCancel={() => setIsAuthMechanismEdit(false)}
|
||||
onEmailChange={onEmailChange}
|
||||
onSave={handleAuthMechanismUpdate}
|
||||
/>
|
||||
)}
|
||||
|
@ -20,8 +20,9 @@ import BotDetails from './BotDetails.component';
|
||||
|
||||
const revokeTokenHandler = jest.fn();
|
||||
const updateBotsDetails = jest.fn();
|
||||
const onEmailChange = jest.fn();
|
||||
|
||||
const botsData = {
|
||||
const botUserData = {
|
||||
id: 'ea09aed1-0251-4a75-b92a-b65641610c53',
|
||||
name: 'sachinchaurasiyachotey87',
|
||||
fullyQualifiedName: 'sachinchaurasiyachotey87',
|
||||
@ -36,6 +37,26 @@ const botsData = {
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
const botData = {
|
||||
id: '4755f87d-2a53-4376-97e6-fc072f29cf5a',
|
||||
name: 'ingestion-bot',
|
||||
fullyQualifiedName: 'ingestion-bot',
|
||||
displayName: 'ingestion-bot',
|
||||
botUser: {
|
||||
id: 'b91d42cb-2a02-4364-ae80-db08b77f1b0c',
|
||||
type: 'user',
|
||||
name: 'ingestion-bot',
|
||||
fullyQualifiedName: 'ingestion-bot',
|
||||
deleted: false,
|
||||
href: 'http://localhost:8585/api/v1/users/b91d42cb-2a02-4364-ae80-db08b77f1b0c',
|
||||
},
|
||||
version: 0.1,
|
||||
updatedAt: 1664267598781,
|
||||
updatedBy: 'ingestion-bot',
|
||||
href: 'http://localhost:8585/api/v1/bots/4755f87d-2a53-4376-97e6-fc072f29cf5a',
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
const mockAuthMechanism = {
|
||||
config: {
|
||||
JWTToken:
|
||||
@ -48,7 +69,8 @@ const mockAuthMechanism = {
|
||||
};
|
||||
|
||||
const mockProp = {
|
||||
botsData,
|
||||
botUserData,
|
||||
botData,
|
||||
botPermission: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
@ -60,6 +82,7 @@ const mockProp = {
|
||||
} as OperationPermission,
|
||||
revokeTokenHandler,
|
||||
updateBotsDetails,
|
||||
onEmailChange,
|
||||
};
|
||||
|
||||
jest.mock('../../utils/PermissionsUtils', () => ({
|
||||
@ -68,7 +91,9 @@ jest.mock('../../utils/PermissionsUtils', () => ({
|
||||
|
||||
jest.mock('../../axiosAPIs/userAPI', () => {
|
||||
return {
|
||||
updateUser: jest.fn().mockImplementation(() => Promise.resolve(botsData)),
|
||||
createUserWithPut: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(botUserData)),
|
||||
getAuthMechanismForBotUser: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve(mockAuthMechanism)),
|
||||
|
@ -24,9 +24,12 @@ import {
|
||||
PAGE_SIZE_LARGE,
|
||||
} from '../../constants/constants';
|
||||
import { BOTS_DOCS } from '../../constants/docs.constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import {
|
||||
INGESTION_BOT_CANT_BE_DELETED,
|
||||
NO_PERMISSION_FOR_ACTION,
|
||||
} from '../../constants/HelperTextUtil';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { Bot } from '../../generated/entity/bot';
|
||||
import { Bot, BotType } from '../../generated/entity/bot';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { Include } from '../../generated/type/include';
|
||||
import { Paging } from '../../generated/type/paging';
|
||||
@ -114,11 +117,9 @@ const BotListV1 = ({
|
||||
render: (_, record) => (
|
||||
<Link
|
||||
className="hover:tw-underline tw-cursor-pointer"
|
||||
data-testid={`bot-link-${getEntityName(record?.botUser)}`}
|
||||
to={getBotsPath(
|
||||
record?.botUser?.fullyQualifiedName || record?.botUser?.name || ''
|
||||
)}>
|
||||
{getEntityName(record?.botUser)}
|
||||
data-testid={`bot-link-${getEntityName(record)}`}
|
||||
to={getBotsPath(record?.fullyQualifiedName || record?.name || '')}>
|
||||
{getEntityName(record)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
@ -127,10 +128,8 @@ const BotListV1 = ({
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
render: (_, record) =>
|
||||
record?.botUser?.description ? (
|
||||
<RichTextEditorPreviewer
|
||||
markdown={record?.botUser?.description || ''}
|
||||
/>
|
||||
record?.description ? (
|
||||
<RichTextEditorPreviewer markdown={record?.description || ''} />
|
||||
) : (
|
||||
<span data-testid="no-description">No Description</span>
|
||||
),
|
||||
@ -140,27 +139,35 @@ const BotListV1 = ({
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Space align="center" size={8}>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={deletePermission ? 'Delete' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
data-testid={`bot-delete-${getEntityName(record?.botUser)}`}
|
||||
disabled={!deletePermission}
|
||||
icon={
|
||||
<SVGIcons
|
||||
alt="Delete"
|
||||
className="tw-w-4"
|
||||
icon={Icons.DELETE}
|
||||
/>
|
||||
}
|
||||
type="text"
|
||||
onClick={() => setSelectedUser(record)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
),
|
||||
render: (_, record) => {
|
||||
const isIngestionBot = record.botType === BotType.IngestionBot;
|
||||
const title = isIngestionBot
|
||||
? INGESTION_BOT_CANT_BE_DELETED
|
||||
: deletePermission
|
||||
? 'Delete'
|
||||
: NO_PERMISSION_FOR_ACTION;
|
||||
const isDisabled = !deletePermission || isIngestionBot;
|
||||
|
||||
return (
|
||||
<Space align="center" size={8}>
|
||||
<Tooltip placement="bottom" title={title}>
|
||||
<Button
|
||||
data-testid={`bot-delete-${getEntityName(record)}`}
|
||||
disabled={isDisabled}
|
||||
icon={
|
||||
<SVGIcons
|
||||
alt="Delete"
|
||||
className="tw-w-4"
|
||||
icon={Icons.DELETE}
|
||||
/>
|
||||
}
|
||||
type="text"
|
||||
onClick={() => setSelectedUser(record)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[]
|
||||
|
@ -28,7 +28,7 @@ import { isUndefined } from 'lodash';
|
||||
import { EditorContentRef } from 'Models';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { generateRandomPwd } from '../../axiosAPIs/auth-API';
|
||||
import { checkEmailInUse, generateRandomPwd } from '../../axiosAPIs/auth-API';
|
||||
import { getBotsPagePath, getUsersPagePath } from '../../constants/constants';
|
||||
import { passwordErrorMessage } from '../../constants/error-message';
|
||||
import {
|
||||
@ -321,11 +321,6 @@ const CreateUser = ({
|
||||
email: email,
|
||||
isAdmin: isAdmin,
|
||||
isBot: isBot,
|
||||
password: isPasswordGenerated ? generatedPassword : password,
|
||||
confirmPassword: isPasswordGenerated
|
||||
? generatedPassword
|
||||
: confirmPassword,
|
||||
createPasswordType: CreatePasswordType.Admincreate,
|
||||
...(forceBot
|
||||
? {
|
||||
authenticationMechanism: {
|
||||
@ -343,7 +338,13 @@ const CreateUser = ({
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
: {
|
||||
password: isPasswordGenerated ? generatedPassword : password,
|
||||
confirmPassword: isPasswordGenerated
|
||||
? generatedPassword
|
||||
: confirmPassword,
|
||||
createPasswordType: CreatePasswordType.Admincreate,
|
||||
}),
|
||||
};
|
||||
onSave(userProfile);
|
||||
};
|
||||
@ -691,14 +692,26 @@ const CreateUser = ({
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
pattern: validEmailRegEx,
|
||||
required: true,
|
||||
type: 'email',
|
||||
message: jsonData['form-error-messages']['empty-email'],
|
||||
message: jsonData['form-error-messages']['invalid-email'],
|
||||
},
|
||||
{
|
||||
pattern: validEmailRegEx,
|
||||
type: 'email',
|
||||
message: jsonData['form-error-messages']['invalid-email'],
|
||||
required: true,
|
||||
validator: async (_, value) => {
|
||||
if (validEmailRegEx.test(value) && !forceBot) {
|
||||
const isEmailAlreadyExists = await checkEmailInUse(value);
|
||||
if (isEmailAlreadyExists) {
|
||||
return Promise.reject(
|
||||
jsonData['form-error-messages']['email-is-in-use']
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
@ -781,111 +794,111 @@ const CreateUser = ({
|
||||
<RichTextEditor initialValue={description} ref={markdownRef} />
|
||||
</Form.Item>
|
||||
|
||||
{isAuthProviderBasic && (
|
||||
<>
|
||||
<Radio.Group
|
||||
name="passwordGenerator"
|
||||
value={passwordGenerator}
|
||||
onChange={handleOnChange}>
|
||||
<Radio value={CreatePasswordGenerator.AutomatciGenerate}>
|
||||
Automatic Generate
|
||||
</Radio>
|
||||
<Radio value={CreatePasswordGenerator.CreatePassword}>
|
||||
Create Password
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
|
||||
{passwordGenerator === CreatePasswordGenerator.CreatePassword ? (
|
||||
<div className="m-t-sm">
|
||||
<Form.Item
|
||||
label="Password"
|
||||
name="password"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
pattern: passwordRegex,
|
||||
message: passwordErrorMessage,
|
||||
},
|
||||
]}>
|
||||
<Input.Password
|
||||
name="password"
|
||||
placeholder="Enter a Password"
|
||||
value={password}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Confirm Password"
|
||||
name="confirmPassword"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (value !== password) {
|
||||
return Promise.reject("Password doesn't match");
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<Input.Password
|
||||
name="confirmPassword"
|
||||
placeholder="Confirm Password"
|
||||
value={confirmPassword}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : (
|
||||
<div className="m-t-sm">
|
||||
<Form.Item
|
||||
label="Generated Password"
|
||||
name="generatedPassword"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
<Input
|
||||
readOnly
|
||||
addonAfter={
|
||||
<div className="flex-center w-16">
|
||||
<div
|
||||
className="w-8 h-7 flex-center cursor-pointer"
|
||||
data-testid="password-generator"
|
||||
onClick={generateRandomPassword}>
|
||||
{isPasswordGenerating ? (
|
||||
<Loader size="small" type="default" />
|
||||
) : (
|
||||
<SVGIcons
|
||||
alt="generate"
|
||||
icon={Icons.SYNC}
|
||||
width="16"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-8 h-7 flex-center">
|
||||
<CopyToClipboardButton
|
||||
copyText={generatedPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
name="generatedPassword"
|
||||
value={generatedPassword}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!forceBot && (
|
||||
<>
|
||||
{isAuthProviderBasic && (
|
||||
<>
|
||||
<Radio.Group
|
||||
name="passwordGenerator"
|
||||
value={passwordGenerator}
|
||||
onChange={handleOnChange}>
|
||||
<Radio value={CreatePasswordGenerator.AutomatciGenerate}>
|
||||
Automatic Generate
|
||||
</Radio>
|
||||
<Radio value={CreatePasswordGenerator.CreatePassword}>
|
||||
Create Password
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
|
||||
{passwordGenerator ===
|
||||
CreatePasswordGenerator.CreatePassword ? (
|
||||
<div className="m-t-sm">
|
||||
<Form.Item
|
||||
label="Password"
|
||||
name="password"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
pattern: passwordRegex,
|
||||
message: passwordErrorMessage,
|
||||
},
|
||||
]}>
|
||||
<Input.Password
|
||||
name="password"
|
||||
placeholder="Enter a Password"
|
||||
value={password}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Confirm Password"
|
||||
name="confirmPassword"
|
||||
rules={[
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (value !== password) {
|
||||
return Promise.reject("Password doesn't match");
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<Input.Password
|
||||
name="confirmPassword"
|
||||
placeholder="Confirm Password"
|
||||
value={confirmPassword}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : (
|
||||
<div className="m-t-sm">
|
||||
<Form.Item
|
||||
label="Generated Password"
|
||||
name="generatedPassword"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}>
|
||||
<Input.Password
|
||||
readOnly
|
||||
addonAfter={
|
||||
<div className="flex-center w-16">
|
||||
<div
|
||||
className="w-8 h-7 flex-center cursor-pointer"
|
||||
data-testid="password-generator"
|
||||
onClick={generateRandomPassword}>
|
||||
{isPasswordGenerating ? (
|
||||
<Loader size="small" type="default" />
|
||||
) : (
|
||||
<SVGIcons
|
||||
alt="generate"
|
||||
icon={Icons.SYNC}
|
||||
width="16"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-8 h-7 flex-center">
|
||||
<CopyToClipboardButton
|
||||
copyText={generatedPassword}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
name="generatedPassword"
|
||||
value={generatedPassword}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Form.Item label="Teams" name="teams">
|
||||
<TeamsSelectable onSelectionChange={setSelectedTeams} />
|
||||
</Form.Item>
|
||||
|
@ -63,7 +63,7 @@ describe('Test manage button component', () => {
|
||||
});
|
||||
|
||||
it('Should render delete modal component on click of delete option', async () => {
|
||||
render(<ManageButton {...mockProps} />);
|
||||
render(<ManageButton {...mockProps} canDelete />);
|
||||
|
||||
const manageButton = await screen.findByTestId('manage-button');
|
||||
|
||||
|
@ -51,3 +51,9 @@ export const NO_PERMISSION_TO_VIEW =
|
||||
|
||||
export const GROUP_TEAM_TYPE_CHANGE_MSG =
|
||||
"The team type 'Group' cannot be changed. Please create a new team with the preferred type.";
|
||||
|
||||
export const INGESTION_BOT_CANT_BE_DELETED =
|
||||
'You can not delete the ingestion bot.';
|
||||
|
||||
export const BOT_ACCOUNT_EMAIL_CHANGE_CONFIRMATION =
|
||||
'Changing account email will update or create a new bot user';
|
||||
|
@ -193,6 +193,7 @@ const jsonData = {
|
||||
'invalid-email': 'Email is invalid.',
|
||||
'invalid-url': 'Url is invalid.',
|
||||
'is-required': 'is required',
|
||||
'email-is-in-use': 'Email is already in use!',
|
||||
},
|
||||
label: {
|
||||
'delete-entity-text':
|
||||
|
@ -31,6 +31,26 @@ const mockUserDetail = {
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
const botData = {
|
||||
id: '4755f87d-2a53-4376-97e6-fc072f29cf5a',
|
||||
name: 'ingestion-bot',
|
||||
fullyQualifiedName: 'ingestion-bot',
|
||||
displayName: 'ingestion-bot',
|
||||
botUser: {
|
||||
id: 'b91d42cb-2a02-4364-ae80-db08b77f1b0c',
|
||||
type: 'user',
|
||||
name: 'ingestion-bot',
|
||||
fullyQualifiedName: 'ingestion-bot',
|
||||
deleted: false,
|
||||
href: 'http://localhost:8585/api/v1/users/b91d42cb-2a02-4364-ae80-db08b77f1b0c',
|
||||
},
|
||||
version: 0.1,
|
||||
updatedAt: 1664267598781,
|
||||
updatedBy: 'ingestion-bot',
|
||||
href: 'http://localhost:8585/api/v1/bots/4755f87d-2a53-4376-97e6-fc072f29cf5a',
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
jest.mock('../../components/BotDetails/BotDetails.component', () => {
|
||||
return jest
|
||||
.fn()
|
||||
@ -38,6 +58,7 @@ jest.mock('../../components/BotDetails/BotDetails.component', () => {
|
||||
});
|
||||
|
||||
jest.mock('../../axiosAPIs/userAPI', () => ({
|
||||
getBotByName: jest.fn().mockImplementation(() => Promise.resolve(botData)),
|
||||
getUserByName: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
revokeUserToken: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
updateUserDetail: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
|
@ -17,9 +17,10 @@ import { compare } from 'fast-json-patch';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
getBotByName,
|
||||
getUserByName,
|
||||
revokeUserToken,
|
||||
updateUserDetail,
|
||||
updateBotDetail,
|
||||
} from '../../axiosAPIs/userAPI';
|
||||
import BotDetails from '../../components/BotDetails/BotDetails.component';
|
||||
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||
@ -32,6 +33,7 @@ import {
|
||||
} from '../../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { UserDetails } from '../../components/Users/Users.interface';
|
||||
import { NO_PERMISSION_TO_VIEW } from '../../constants/HelperTextUtil';
|
||||
import { Bot } from '../../generated/entity/bot';
|
||||
import { User } from '../../generated/entity/teams/user';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
|
||||
@ -40,7 +42,8 @@ import { showErrorToast } from '../../utils/ToastUtils';
|
||||
const BotDetailsPage = () => {
|
||||
const { botsName } = useParams<{ [key: string]: string }>();
|
||||
const { getEntityPermissionByFqn } = usePermissionProvider();
|
||||
const [botsData, setBotsData] = useState<User>({} as User);
|
||||
const [botUserData, setBotUserData] = useState<User>({} as User);
|
||||
const [botData, setBotData] = useState<Bot>({} as Bot);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [botPermission, setBotPermission] = useState<OperationPermission>(
|
||||
@ -62,34 +65,32 @@ const BotDetailsPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchBotsData = () => {
|
||||
setIsLoading(true);
|
||||
getUserByName(botsName)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
setBotsData(res);
|
||||
} else {
|
||||
throw jsonData['api-error-messages']['unexpected-server-response'];
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
jsonData['api-error-messages']['fetch-user-details-error']
|
||||
);
|
||||
setIsError(true);
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
const fetchBotsData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const botResponse = await getBotByName(botsName);
|
||||
|
||||
const botUserResponse = await getUserByName(
|
||||
botResponse.botUser.fullyQualifiedName || ''
|
||||
);
|
||||
setBotUserData(botUserResponse);
|
||||
setBotData(botResponse);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
setIsError(true);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updateBotsDetails = async (data: UserDetails) => {
|
||||
const updatedDetails = { ...botsData, ...data };
|
||||
const jsonPatch = compare(botsData, updatedDetails);
|
||||
const updatedDetails = { ...botData, ...data };
|
||||
const jsonPatch = compare(botData, updatedDetails);
|
||||
|
||||
try {
|
||||
const response = await updateUserDetail(botsData.id, jsonPatch);
|
||||
const response = await updateBotDetail(botData.id, jsonPatch);
|
||||
if (response) {
|
||||
setBotsData((prevData) => ({
|
||||
setBotData((prevData) => ({
|
||||
...prevData,
|
||||
...response,
|
||||
}));
|
||||
@ -102,10 +103,10 @@ const BotDetailsPage = () => {
|
||||
};
|
||||
|
||||
const revokeBotsToken = () => {
|
||||
revokeUserToken(botsData.id)
|
||||
revokeUserToken(botUserData.id)
|
||||
.then((res) => {
|
||||
const data = res;
|
||||
setBotsData(data);
|
||||
setBotUserData(data);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(err);
|
||||
@ -129,10 +130,12 @@ const BotDetailsPage = () => {
|
||||
} else {
|
||||
return (
|
||||
<BotDetails
|
||||
botData={botData}
|
||||
botPermission={botPermission}
|
||||
botsData={botsData}
|
||||
botUserData={botUserData}
|
||||
revokeTokenHandler={revokeBotsToken}
|
||||
updateBotsDetails={updateBotsDetails}
|
||||
onEmailChange={fetchBotsData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -16,9 +16,13 @@ import { observer } from 'mobx-react';
|
||||
import { LoadingState } from 'Models';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { createBot } from '../../axiosAPIs/botsAPI';
|
||||
import { createBotWithPut } from '../../axiosAPIs/botsAPI';
|
||||
import { getRoles } from '../../axiosAPIs/rolesAPIV1';
|
||||
import { createUser } from '../../axiosAPIs/userAPI';
|
||||
import {
|
||||
createUser,
|
||||
createUserWithPut,
|
||||
getBotByName,
|
||||
} from '../../axiosAPIs/userAPI';
|
||||
import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import CreateUserComponent from '../../components/CreateUser/CreateUser.component';
|
||||
import { PAGE_SIZE_LARGE } from '../../constants/constants';
|
||||
@ -72,56 +76,86 @@ const CreateUserPage = () => {
|
||||
setStatus('initial');
|
||||
};
|
||||
|
||||
const checkBotInUse = async (name: string) => {
|
||||
try {
|
||||
const response = await getBotByName(name);
|
||||
|
||||
return Boolean(response);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Submit handler for new user form.
|
||||
* @param userData Data for creating new user
|
||||
*/
|
||||
const handleAddUserSave = (userData: CreateUser) => {
|
||||
setStatus('waiting');
|
||||
createUser(userData)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
if (bot) {
|
||||
createBot({
|
||||
botUser: { id: res.id, type: EntityType.USER },
|
||||
name: res.name,
|
||||
displayName: res.displayName,
|
||||
description: res.description,
|
||||
} as Bot)
|
||||
.then((res) => {
|
||||
setStatus('success');
|
||||
res && showSuccessToast(`Bot created successfully`);
|
||||
setTimeout(() => {
|
||||
setStatus('initial');
|
||||
const handleAddUserSave = async (userData: CreateUser) => {
|
||||
if (bot) {
|
||||
const isBotExists = await checkBotInUse(userData.name);
|
||||
if (isBotExists) {
|
||||
showErrorToast(`${userData.name} bot already exists.`);
|
||||
} else {
|
||||
try {
|
||||
setStatus('waiting');
|
||||
// Create a user with isBot:true
|
||||
const userResponse = await createUserWithPut({
|
||||
...userData,
|
||||
botName: userData.name,
|
||||
});
|
||||
|
||||
goToUserListPage();
|
||||
}, 500);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
handleSaveFailure(
|
||||
err,
|
||||
jsonData['api-error-messages']['create-bot-error']
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Create a bot entity with botUser data
|
||||
const botResponse = await createBotWithPut({
|
||||
botUser: { id: userResponse.id, type: EntityType.USER },
|
||||
name: userResponse.name,
|
||||
displayName: userResponse.displayName,
|
||||
description: userResponse.description,
|
||||
} as Bot);
|
||||
|
||||
if (botResponse) {
|
||||
setStatus('success');
|
||||
showSuccessToast(`Bot created successfully`);
|
||||
setTimeout(() => {
|
||||
setStatus('initial');
|
||||
|
||||
goToUserListPage();
|
||||
}, 500);
|
||||
} else {
|
||||
handleSaveFailure(
|
||||
jsonData['api-error-messages']['create-bot-error']
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
handleSaveFailure(
|
||||
error as AxiosError,
|
||||
jsonData['api-error-messages']['create-bot-error']
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
setStatus('waiting');
|
||||
|
||||
const response = await createUser(userData);
|
||||
|
||||
if (response) {
|
||||
setStatus('success');
|
||||
setTimeout(() => {
|
||||
setStatus('initial');
|
||||
goToUserListPage();
|
||||
}, 500);
|
||||
} else {
|
||||
handleSaveFailure(
|
||||
jsonData['api-error-messages']['create-user-error']
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
} catch (error) {
|
||||
handleSaveFailure(
|
||||
err,
|
||||
error as AxiosError,
|
||||
jsonData['api-error-messages']['create-user-error']
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fetchRoles = async () => {
|
||||
|
@ -14,8 +14,9 @@
|
||||
import { isUndefined } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { AuthTypes } from '../enums/signin.enum';
|
||||
import { AuthenticationMechanism } from '../generated/api/teams/createUser';
|
||||
import { SsoServiceType } from '../generated/entity/teams/authN/ssoAuth';
|
||||
import { AuthType, JWTTokenExpiry } from '../generated/entity/teams/user';
|
||||
import { AuthType, JWTTokenExpiry, User } from '../generated/entity/teams/user';
|
||||
|
||||
export const getJWTTokenExpiryOptions = () => {
|
||||
return Object.keys(JWTTokenExpiry).map((expiry) => {
|
||||
@ -114,9 +115,34 @@ export const getTokenExpiry = (expiry: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const DEFAULT_GOOGLE_SSO_CLIENT_CONFIG = {
|
||||
secretKey: '',
|
||||
audience: 'https://www.googleapis.com/oauth2/v4/token',
|
||||
};
|
||||
export const getAuthMechanismFormInitialValues = (
|
||||
authMechanism: AuthenticationMechanism,
|
||||
botUser: User
|
||||
) => {
|
||||
const authConfig = authMechanism.config?.authConfig;
|
||||
const email = botUser.email;
|
||||
|
||||
export const SECRET_KEY_ERROR_MSG = 'SecretKey is required!';
|
||||
return {
|
||||
audience: authConfig?.audience,
|
||||
secretKey: authConfig?.secretKey,
|
||||
|
||||
clientId: authConfig?.clientId,
|
||||
|
||||
oktaEmail: authConfig?.email,
|
||||
|
||||
orgURL: authConfig?.orgURL,
|
||||
|
||||
privateKey: authConfig?.privateKey,
|
||||
|
||||
scopes: authConfig?.scopes?.join(','),
|
||||
|
||||
domain: authConfig?.domain,
|
||||
|
||||
authority: authConfig?.authority,
|
||||
|
||||
clientSecret: authConfig?.clientSecret,
|
||||
|
||||
tokenEndpoint: authConfig?.tokenEndpoint,
|
||||
email,
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user