Skip to content

Commit

Permalink
feat(console): integrate jwt customizer test api
Browse files Browse the repository at this point in the history
integrate jwt customizer test api
  • Loading branch information
simeng-li committed Mar 20, 2024
1 parent f638c8e commit dade0bc
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function MonacoCodeEditor({
const isMultiModals = useMemo(() => models.length > 1, [models]);

// Get the container ref and the editor height
const { containerRef, editorHeight } = useEditorHeight();
const { containerRef, headerRef, editorHeight } = useEditorHeight();

useEffect(() => {
// Monaco will be ready after the editor is mounted, useEffect will be called after the monaco is ready
Expand Down Expand Up @@ -108,8 +108,8 @@ function MonacoCodeEditor({
);

return (
<div className={classNames(className, styles.codeEditor)}>
<header>
<div ref={containerRef} className={classNames(className, styles.codeEditor)}>
<header ref={headerRef}>
<div className={styles.tabList}>
{models.map(({ name, title, icon }) => (
<div
Expand Down Expand Up @@ -159,7 +159,7 @@ function MonacoCodeEditor({
)}
</div>
</header>
<div ref={containerRef} className={styles.editorContainer}>
<div className={styles.editorContainer}>
{activeModel && (
<Editor
height={editorHeight}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import { useRef, useState, useLayoutEffect } from 'react';
// @see {@link https://github.com/react-monaco-editor/react-monaco-editor/issues/391}
const useEditorHeight = () => {
const containerRef = useRef<HTMLDivElement>(null);
const headerRef = useRef<HTMLDivElement>(null);

const [editorHeight, setEditorHeight] = useState<number | string>('100%');
const safeArea = 16;

useLayoutEffect(() => {
const handleResize = () => {
const safeAreaHeight = headerRef.current?.clientHeight
? headerRef.current.clientHeight + safeArea
: safeArea;

if (containerRef.current) {
setEditorHeight(containerRef.current.clientHeight - safeArea);
setEditorHeight(containerRef.current.clientHeight - safeAreaHeight);
}
};

Expand All @@ -29,7 +34,7 @@ const useEditorHeight = () => {
};
}, []);

return { containerRef, editorHeight };
return { containerRef, headerRef, editorHeight };
};

export default useEditorHeight;
39 changes: 29 additions & 10 deletions packages/console/src/pages/JwtClaims/SettingsSection/TestTab.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { LogtoJwtTokenPath } from '@logto/schemas';
import { type JsonObject, LogtoJwtTokenPath } from '@logto/schemas';
import classNames from 'classnames';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useFormContext, Controller, type ControllerRenderProps } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import Button from '@/ds-components/Button';
import Card from '@/ds-components/Card';
import useApi from '@/hooks/use-api';

import MonacoCodeEditor, { type ModelControl } from '../MonacoCodeEditor/index.js';
import { type JwtClaimsFormType } from '../type.js';
import MonacoCodeEditor, { type ModelControl } from '../MonacoCodeEditor';
import { type JwtClaimsFormType } from '../type';
import {
accessTokenPayloadTestModel,
clientCredentialsPayloadTestModel,
userContextTestModel,
} from '../utils/config.js';
} from '../utils/config';
import { formatFormDataToTestRequestPayload } from '../utils/format';

import TestResult, { type TestResultData } from './TestResult.js';
import TestResult, { type TestResultData } from './TestResult';
import * as styles from './index.module.scss';

type Props = {
Expand All @@ -24,13 +26,15 @@ type Props = {

const userTokenModelSettings = [accessTokenPayloadTestModel, userContextTestModel];
const machineToMachineTokenModelSettings = [clientCredentialsPayloadTestModel];
const testEndpointPath = 'api/config/jwt-customizer/test';

function TestTab({ isActive }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.jwt_claims' });
const [testResult, setTestResult] = useState<TestResultData>();
const [activeModelName, setActiveModelName] = useState<string>();
const api = useApi({ hideErrorToast: true });

const { watch, control, formState } = useFormContext<JwtClaimsFormType>();
const { watch, control, formState, getValues } = useFormContext<JwtClaimsFormType>();
const tokenType = watch('tokenType');

const editorModels = useMemo(
Expand All @@ -45,9 +49,24 @@ function TestTab({ isActive }: Props) {
setActiveModelName(editorModels[0]?.name);
}, [editorModels, tokenType]);

const onTestHandler = useCallback(() => {
// TODO: API integration, read form data and send the request to the server
}, []);
const onTestHandler = useCallback(async () => {
const payload = getValues();

const result = await api
.post(testEndpointPath, {
json: formatFormDataToTestRequestPayload(payload),
})
.json<JsonObject>()
.catch((error: unknown) => {
setTestResult({
error: error instanceof Error ? error.message : String(error),
});
});

if (result) {
setTestResult({ payload: JSON.stringify(result, null, 2) });
}
}, [api, getValues]);

const getModelControllerProps = useCallback(
({ value, onChange }: ControllerRenderProps<JwtClaimsFormType, 'testSample'>): ModelControl => {
Expand Down Expand Up @@ -124,7 +143,7 @@ function TestTab({ isActive }: Props) {
}}
render={({ field }) => (
<MonacoCodeEditor
className={styles.flexGrow}
className={testResult ? styles.shrinkCodeEditor : styles.flexGrow}
enabledActions={['restore', 'copy']}
models={editorModels}
activeModelName={activeModelName}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,13 @@
margin-bottom: _.unit(4);
}

.shrinkCodeEditor {
height: 50%;
}

.testResult {
margin-top: _.unit(3);
height: calc(50% - _.unit(3));
flex: 1;
background-color: var(--color-bg-layer-2);
border-radius: 8px;
border: 1px solid var(--color-divider);
Expand Down Expand Up @@ -197,5 +201,5 @@
}

.flexGrow {
flex-grow: 1;
flex: 1;
}
6 changes: 3 additions & 3 deletions packages/console/src/pages/JwtClaims/utils/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,19 +192,19 @@ const standardTokenPayloadData = {
aud: 'http://localhost:3000/api/test',
};

const defaultAccessTokenPayload: AccessTokenPayload = {
export const defaultAccessTokenPayload: AccessTokenPayload = {
...standardTokenPayloadData,
grantId: 'grant_123',
accountId: 'uid_123',
kind: 'AccessToken',
};

const defaultClientCredentialsPayload: ClientCredentialsPayload = {
export const defaultClientCredentialsPayload: ClientCredentialsPayload = {
...standardTokenPayloadData,
kind: 'ClientCredentials',
};

const defaultUserTokenContextData = {
export const defaultUserTokenContextData = {
user: {
id: '123',
primaryEmail: 'foo@logto.io',
Expand Down
26 changes: 25 additions & 1 deletion packages/console/src/pages/JwtClaims/utils/format.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import {
type LogtoJwtTokenPath,
LogtoJwtTokenPath,
type AccessTokenJwtCustomizer,
type ClientCredentialsJwtCustomizer,
} from '@logto/schemas';

import type { JwtClaimsFormType } from '../type';

import {
defaultAccessTokenPayload,
defaultClientCredentialsPayload,
defaultUserTokenContextData,
} from './config';

const formatEnvVariablesResponseToFormData = (
enVariables?: AccessTokenJwtCustomizer['envVars']
) => {
Expand Down Expand Up @@ -80,5 +86,23 @@ export const formatFormDataToRequestData = (data: JwtClaimsFormType) => {
};
};

export const formatFormDataToTestRequestPayload = (data: JwtClaimsFormType) => {
const defaultTokenPayload =
data.tokenType === LogtoJwtTokenPath.AccessToken
? defaultAccessTokenPayload
: defaultClientCredentialsPayload;

const defaultContext =
data.tokenType === LogtoJwtTokenPath.AccessToken ? defaultUserTokenContextData : undefined;

return {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- parse empty string as undefined
script: data.script || undefined,
envVars: formatEnvVariablesFormData(data.environmentVariables),
token: formatSampleCodeStringToJson(data.testSample?.tokenSample) ?? defaultTokenPayload,
context: formatSampleCodeStringToJson(data.testSample?.contextSample) ?? defaultContext,
};
};

export const getApiPath = (tokenType: LogtoJwtTokenPath) =>
`api/configs/jwt-customizer/${tokenType}`;

0 comments on commit dade0bc

Please sign in to comment.