Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions apps/ai-dial-admin/src/app/[lang]/assets-toolsets/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const dynamic = 'force-dynamic';

export default async function Page(params: {
params: Promise<{ id: string }>;
searchParams: Promise<{ path: string; code?: string }>;
searchParams: Promise<{ path: string; code?: string; isUser?: string }>;
}) {
const isEnableAuth = getIsEnableAuthToggle();
const token = await getUserToken(isEnableAuth, headers(), cookies());
Expand All @@ -32,6 +32,7 @@ export default async function Page(params: {
}

let oAuthCode = null;
let isUser = false;
let etag = DEFAULT_ETAG;

let toolsets: AssetToolset[] = [];
Expand All @@ -40,6 +41,7 @@ export default async function Page(params: {
try {
const searchParams = await params.searchParams;
oAuthCode = searchParams.code;
isUser = searchParams.isUser === 'true';
const path = decodeURIComponent(searchParams.path);
const name = decodeURIComponent((await params.params).id);

Expand All @@ -65,7 +67,13 @@ export default async function Page(params: {
return (
<SaveValidationContextProvider>
<ToolsetFolderProvider>
<ToolsetView oAuthCode={oAuthCode} etag={etag} originalToolset={toolset} toolsets={toolsets || []} />
<ToolsetView
oAuthCode={oAuthCode}
isUserLevel={isUser}
etag={etag}
originalToolset={toolset}
toolsets={toolsets || []}
/>
</ToolsetFolderProvider>
</SaveValidationContextProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,14 @@ export async function getAssetTools(name: string) {
return assetsApi.getTools(name, token);
}

export async function signInToolset(toolset: AssetToolset, type: ToolsetAuthCredentialLevel, authCode?: string) {
export async function signInToolset(
toolset: AssetToolset,
type: ToolsetAuthCredentialLevel,
apiKey?: string,
authCode?: string,
) {
const token = await getUserToken(getIsEnableAuthToggle(), headers(), cookies());
return assetsApi.signInToolset(toolset, type, token, authCode);
return assetsApi.signInToolset(toolset, type, token, apiKey, authCode);
}

export async function signOutToolset(toolset: AssetToolset, type: ToolsetAuthCredentialLevel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,29 @@ import { Toolset } from '@/src/models/dial/toolset';
import { ApplicationRoute } from '@/src/types/routes';
import { getErrorNotification } from '@/src/utils/notification';
import { modifyNameVersionInPrompt } from '@/src/utils/prompts/versions';
import Authentication from '@/src/components/Toolsets/View/Authentication';

interface Props {
etag: string;
view: ApplicationRoute;
asset: DeploymentAsset;
assets: DeploymentAsset[];
runners: DialApplicationScheme[];
apiKeyValue?: string;
onChangeKeyValue?: (apiKeyValue: string) => void;
onChange: (asset: DeploymentAsset) => void;
}

const DeploymentProperties: FC<Props> = ({ etag, asset, view, assets, runners, onChange }) => {
const DeploymentProperties: FC<Props> = ({
apiKeyValue,
onChangeKeyValue,
etag,
asset,
view,
assets,
runners,
onChange,
}) => {
const t = useI18n() as (t: string) => string;
const router = useRouter();
const { showNotification } = useNotification();
Expand Down Expand Up @@ -123,8 +135,12 @@ const DeploymentProperties: FC<Props> = ({ etag, asset, view, assets, runners, o
{view === ApplicationRoute.AssetsToolsets && (
<>
<ToolsetEndpoint entity={asset as AssetToolset} onChange={onChange as (entity: Toolset) => void} />
{/* // TODO: waiting BE */}
{/* <Authentication toolset={asset as AssetToolset} onChange={onChange as (entity: Toolset) => void} /> */}
<Authentication
onChangeKeyValue={onChangeKeyValue}
apiKeyValue={apiKeyValue}
toolset={asset as AssetToolset}
onChange={onChange as (entity: Toolset) => void}
/>
</>
)}
{view === ApplicationRoute.AssetsApplications && (
Expand Down
55 changes: 35 additions & 20 deletions apps/ai-dial-admin/src/components/Assets/Toolsets/View.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import { useRouter } from 'next/navigation';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';

import { DialTabs } from '@epam/ai-dial-ui-kit';
import { ButtonVariant, DialButton, DialTabs } from '@epam/ai-dial-ui-kit';
import classNames from 'classnames';
import { IconLogin, IconLogout } from '@tabler/icons-react';
import { cloneDeep } from 'lodash';

import {
Expand Down Expand Up @@ -35,18 +36,25 @@ import { addTrailingSlash, changePath, getListOfPathsToMove, removeTrailingSlash
import { isEqualSkippingUndefined } from '@/src/utils/is-equals-entity';
import { getErrorNotification, getSuccessNotification } from '@/src/utils/notification';
import { getUrnForEntity } from '@/src/utils/open-in-new-tab';
import { encodeToolsetRedirectState, isLoggedInToToolset } from '@/src/utils/toolset/toolset-auth';
import {
encodeToolsetRedirectState,
isLoggedInToToolset,
isUserLoggedInToToolset,
} from '@/src/utils/toolset/toolset-auth';
import LoginPopup from './LoginPopup';
import { getUpdateNotificationDescription, getUpdateNotificationTitle } from '@/src/utils/entities/update-entity';

import { ToolsetI18nKey } from '@/src/constants/i18n';
import { BASE_ICON_PROPS } from '@/src/constants/main-layout';
let isSignInProcessed = false;
interface Props {
etag: string;
oAuthCode?: string | null;
isUserLevel?: boolean;
originalToolset: AssetToolset;
toolsets: AssetToolset[];
}

const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets }) => {
const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets, isUserLevel }) => {
const t = useI18n() as (stringToTranslate: string) => string;
const tabs = [propertiesTabs(t), toolsTabs(t)];
const router = useRouter();
Expand All @@ -59,8 +67,8 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
const [isChanged, setIsChanged] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [jsonEditorEnabled, setJsonEditorEnabled] = useState(false);
const [apiKeyValue, setApiKeyValue] = useState<string>('');

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const isToolsetSignedIn = useMemo(() => {
return isLoggedInToToolset(selectedToolset);
}, [selectedToolset]);
Expand Down Expand Up @@ -165,15 +173,16 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })

const signIn = useCallback(
(type: ToolsetAuthCredentialLevel, code?: string) => {
signInToolset(selectedToolset, type, code).then((res) => {
if (res.success) {
router.push(getUrnForEntity(ApplicationRoute.AssetsToolsets, selectedToolset));
} else {
isSignInProcessed = true;
signInToolset(selectedToolset, type, apiKeyValue, code).then((res) => {
isSignInProcessed = false;
if (!res.success) {
showNotification(getErrorNotification(res.errorHeader, res.errorMessage));
}
router.push(getUrnForEntity(ApplicationRoute.AssetsToolsets, selectedToolset));
});
},
[router, selectedToolset, showNotification],
[router, selectedToolset, showNotification, apiKeyValue],
);

const onLogin = useCallback(
Expand All @@ -193,9 +202,11 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })

url.searchParams.set(
'redirect_uri',
`${window.location.origin}${getUrnForEntity(ApplicationRoute.AssetsToolsets, selectedToolset)}`,
`${window.location.origin}${getUrnForEntity(ApplicationRoute.AssetsToolsets, selectedToolset)}&isUser=${type === ToolsetAuthCredentialLevel.USER}`,
);

if (authSettings.codeChallenge) {
url.searchParams.set('code_challenge', authSettings.codeChallenge);
}
if (authSettings.codeChallengeMethod) {
url.searchParams.set('code_challenge_method', authSettings.codeChallengeMethod);
}
Expand All @@ -213,9 +224,11 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
[selectedToolset, signIn],
);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const onLogout = useCallback(() => {
signOutToolset(selectedToolset, ToolsetAuthCredentialLevel.GLOBAL).then((res) => {
const level = isUserLoggedInToToolset(selectedToolset)
? ToolsetAuthCredentialLevel.USER
: ToolsetAuthCredentialLevel.GLOBAL;
signOutToolset(selectedToolset, level).then((res) => {
if (res.success) {
router.push(getUrnForEntity(ApplicationRoute.AssetsToolsets, selectedToolset));
} else {
Expand All @@ -225,10 +238,11 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
}, [router, selectedToolset, showNotification]);

useEffect(() => {
if (oAuthCode) {
signIn(ToolsetAuthCredentialLevel.USER, oAuthCode);
if (oAuthCode && !isSignInProcessed) {
signIn(isUserLevel ? ToolsetAuthCredentialLevel.USER : ToolsetAuthCredentialLevel.GLOBAL, oAuthCode);
}
}, [signIn, oAuthCode]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<>
Expand All @@ -252,8 +266,7 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
context={useToolsetFolder as () => AssetsFolderContext<DialFile | AssetToolset>}
childrenContainerClass="flex-row-reverse"
>
{/* TODO: waiting for BE */}
{/* {isToolsetSignedIn ? (
{isToolsetSignedIn ? (
<DialButton
variant={ButtonVariant.Secondary}
title={t(ToolsetI18nKey.LogOut)}
Expand All @@ -267,7 +280,7 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
iconBefore={<IconLogin {...BASE_ICON_PROPS} />}
onClick={() => setIsModalOpen(true)}
/>
)} */}
)}
</HeaderButtons>
</div>
<div className="flex-1 overflow-auto mt-3 min-h-0">
Expand All @@ -290,6 +303,8 @@ const ToolsetView: FC<Props> = ({ oAuthCode, etag, originalToolset, toolsets })
jsonEditorEnabled={jsonEditorEnabled}
isSkipRefresh={false}
onChangeEntity={onChangeEntity}
apiKeyValue={apiKeyValue}
onChangeKeyValue={setApiKeyValue}
/>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ interface Props {
assets?: DeploymentAsset[] | null;
selectedEntity: BaseEntity;
onChangeEntity: (entity: BaseEntity) => void;

// asset toolset specific props
apiKeyValue?: string;
onChangeKeyValue?: (apiKeyValue: string) => void;
}

const PropertiesContent: FC<Props> = ({
Expand All @@ -31,6 +35,8 @@ const PropertiesContent: FC<Props> = ({
etag,
assets,
selectedEntity,
onChangeKeyValue,
apiKeyValue,
onChangeEntity,
}) => {
const getPropertiesView = useCallback(() => {
Expand All @@ -51,6 +57,8 @@ const PropertiesContent: FC<Props> = ({
assets={assets || []}
runners={applicationSchemes || []}
onChange={onChangeEntity}
apiKeyValue={apiKeyValue}
onChangeKeyValue={onChangeKeyValue}
/>
);
}
Expand All @@ -64,7 +72,7 @@ const PropertiesContent: FC<Props> = ({
updateEntity={onChangeEntity}
/>
);
}, [view, selectedEntity, applicationSchemes, names, onChangeEntity, etag, assets]);
}, [view, selectedEntity, applicationSchemes, names, onChangeEntity, etag, assets, apiKeyValue, onChangeKeyValue]);

return (
<div className="flex flex-col h-full w-full">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ interface Props {
key?: number;
setIsChanged?: Dispatch<SetStateAction<boolean>>;
setSelectedEntity?: Dispatch<SetStateAction<BaseEntity>>;

// asset toolset specific props
apiKeyValue?: string;
onChangeKeyValue?: (apiKeyValue: string) => void;
}

const ViewContent: FC<Props> = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import IconControl from '@/src/components/EntityMainProperties/BaseProperties/Ic
import { DialToolsetResource } from '@/src/models/dial/application-resource';
import TopicsControl from '@/src/components/EntityMainProperties/BaseProperties/Topics';
import ToolsetEndpoint from '@/src/components/SourceField/Endpoints/ToolsetEndpoint';
import Authentication from '@/src/components/Toolsets/View/Authentication';

interface Props {
toolset: DialToolsetResource;
Expand All @@ -20,6 +21,7 @@ const ToolsetInfo: FC<Props> = ({ toolset }) => {
<IconControl disabled={true} iconUrl={toolset.iconUrl} />
<TopicsControl disabled={true} entity={{ topics: toolset?.descriptionKeywords }} />
<ToolsetEndpoint disabled={true} entity={toolset} />
<Authentication disabled={true} toolset={toolset} />
</div>
) : null;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
import { DialPasswordInputField } from '@epam/ai-dial-ui-kit';
import { DialPasswordInputField, DialTextInputField } from '@epam/ai-dial-ui-kit';
import { FC } from 'react';

import { EntityFieldsI18nKey, EntityPlaceholdersI18nKey } from '@/src/constants/i18n';
import { useI18n } from '@/src/locales/client';
import { ToolsetAuthSettings } from '@/src/models/dial/toolset';

interface Props {
apiKeyValue?: string;
authSettings?: ToolsetAuthSettings;
onChange: (authSettings: ToolsetAuthSettings) => void;
disabled?: boolean;
onChange?: (authSettings: ToolsetAuthSettings) => void;
onChangeKeyValue?: (apiKeyValue: string) => void;
}

const ApiKeySection: FC<Props> = ({ authSettings, onChange }) => {
const ApiKeySection: FC<Props> = ({ disabled, authSettings, onChange, onChangeKeyValue, apiKeyValue }) => {
const t = useI18n();

return (
<div className="flex flex-col gap-y-4">
<DialTextInputField
elementId="apiKeyHeader"
fieldTitle={t(EntityFieldsI18nKey.apiKeyHeader)}
placeholder={t(EntityPlaceholdersI18nKey.Header)}
value={authSettings?.apiKeyHeader}
disabled={disabled}
onChange={(apiKeyHeader) => onChange?.({ ...(authSettings || {}), apiKeyHeader } as ToolsetAuthSettings)}
/>
<DialPasswordInputField
elementId="apiKeyValue"
fieldTitle={t(EntityFieldsI18nKey.apiKeyHeader)}
fieldTitle={t(EntityFieldsI18nKey.apiKeyValue)}
placeholder={t(EntityPlaceholdersI18nKey.Value)}
value={authSettings?.apiKeyHeader}
onChange={(apiKeyHeader) => onChange({ ...(authSettings || {}), apiKeyHeader } as ToolsetAuthSettings)}
value={apiKeyValue}
disabled={disabled}
onChange={(v) => onChangeKeyValue?.(v || '')}
/>
</div>
);
Expand Down
Loading
Loading