Skip to content

Commit 7c249f0

Browse files
syntrydymoabu
andauthored
fix(admin-ui): Use typescript generated client for Auth Server Logging pages (#2463)
* fix(admin-ui): Use typescript generated client for Auth Server Logging pages #2462 * fix(admin-ui): Use typescript generated client for Auth Server Logging pages #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 * fix(admin-ui): Apply code review #2462 --------- Co-authored-by: Mohammad Abudayyeh <47318409+moabu@users.noreply.github.com>
1 parent f0ac17b commit 7c249f0

File tree

5 files changed

+332
-73
lines changed

5 files changed

+332
-73
lines changed

admin-ui/plugins/auth-server/components/Configuration/Defaults/LoggingPage.test.js renamed to admin-ui/plugins/auth-server/components/Configuration/Defaults/LoggingPage.test.tsx

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import LoggingPage from './LoggingPage'
44
import { Provider } from 'react-redux'
55
import AppTestWrapper from 'Routes/Apps/Gluu/Tests/Components/AppTestWrapper.test'
66
import { combineReducers, configureStore } from '@reduxjs/toolkit'
7+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
8+
import type { Logging } from 'JansConfigApi'
9+
10+
// Mock the orval hooks
11+
jest.mock('JansConfigApi', () => ({
12+
useGetConfigLogging: jest.fn(),
13+
usePutConfigLogging: jest.fn(),
14+
}))
715

816
const permissions = [
917
'https://jans.io/oauth/config/logging.readonly',
@@ -13,37 +21,76 @@ const permissions = [
1321

1422
const AUTH_STATE = {
1523
permissions: permissions,
24+
token: {
25+
access_token: 'test-token',
26+
},
27+
config: {
28+
clientId: 'test-client-id',
29+
},
30+
userinfo: {
31+
inum: 'test-inum',
32+
name: 'Test User',
33+
},
1634
}
17-
const logging = {
35+
36+
const logging: Logging = {
1837
loggingLevel: 'TRACE',
1938
loggingLayout: 'text',
2039
httpLoggingEnabled: true,
2140
disableJdkLogger: false,
2241
enabledOAuthAuditLogging: false,
2342
}
24-
const LOGGING_STATE = {
25-
logging: logging,
26-
loading: false,
27-
}
2843

2944
const store = configureStore({
3045
reducer: combineReducers({
3146
authReducer: (state = AUTH_STATE) => state,
32-
loggingReducer: (state = LOGGING_STATE) => state,
47+
cedarPermissions: (state = { permissions: {} }) => state,
3348
noReducer: (state = {}) => state,
3449
}),
3550
})
3651

37-
const Wrapper = ({ children }) => (
52+
const queryClient = new QueryClient({
53+
defaultOptions: {
54+
queries: {
55+
retry: false,
56+
},
57+
},
58+
})
59+
60+
interface WrapperProps {
61+
children: React.ReactNode
62+
}
63+
64+
const Wrapper: React.FC<WrapperProps> = ({ children }) => (
3865
<AppTestWrapper>
39-
<Provider store={store}>{children}</Provider>
66+
<QueryClientProvider client={queryClient}>
67+
<Provider store={store}>{children}</Provider>
68+
</QueryClientProvider>
4069
</AppTestWrapper>
4170
)
4271

43-
it('Should render Acr page properly', () => {
72+
it('Should render Logging page properly', () => {
73+
const { useGetConfigLogging, usePutConfigLogging } = require('JansConfigApi')
74+
75+
// Mock the GET hook to return logging data
76+
useGetConfigLogging.mockReturnValue({
77+
data: logging,
78+
isLoading: false,
79+
error: null,
80+
})
81+
82+
// Mock the PUT mutation hook
83+
usePutConfigLogging.mockReturnValue({
84+
mutateAsync: jest.fn(),
85+
isPending: false,
86+
isError: false,
87+
error: null,
88+
})
89+
4490
render(<LoggingPage />, {
4591
wrapper: Wrapper,
4692
})
93+
4794
expect(screen.getByTestId('loggingLayout')).toHaveDisplayValue(logging.loggingLayout)
4895
expect(screen.getByTestId('loggingLevel')).toHaveDisplayValue(logging.loggingLevel)
4996
expect(screen.getByTestId('httpLoggingEnabled')).toBeChecked()

admin-ui/plugins/auth-server/components/Configuration/Defaults/LoggingPage.js renamed to admin-ui/plugins/auth-server/components/Configuration/Defaults/LoggingPage.tsx

Lines changed: 133 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,95 +7,173 @@ import GluuCommitDialog from 'Routes/Apps/Gluu/GluuCommitDialog'
77
import GluuFormFooter from 'Routes/Apps/Gluu/GluuFormFooter'
88
import { JSON_CONFIG } from 'Utils/ApiResources'
99
import { loggingValidationSchema } from './validations'
10-
import { LOG_LEVELS, LOG_LAYOUTS, getLoggingInitialValues } from './utils'
10+
import {
11+
LOG_LEVELS,
12+
LOG_LAYOUTS,
13+
getLoggingInitialValues,
14+
getMergedValues,
15+
getChangedFields,
16+
} from './utils'
17+
import type { LoggingFormValues } from './utils'
1118
import applicationStyle from 'Routes/Apps/Gluu/styles/applicationstyle'
12-
import { useDispatch, useSelector } from 'react-redux'
1319
import { Formik } from 'formik'
1420
import { useNavigate } from 'react-router-dom'
15-
import {
16-
getLoggingConfig,
17-
editLoggingConfig,
18-
} from 'Plugins/auth-server/redux/features/loggingSlice'
21+
import { useGetConfigLogging, usePutConfigLogging, type Logging } from 'JansConfigApi'
1922
import { LOGGING_READ, LOGGING_WRITE } from 'Utils/PermChecker'
2023
import { useCedarling } from '@/cedarling'
2124
import { useTranslation } from 'react-i18next'
2225
import SetTitle from 'Utils/SetTitle'
2326
import GluuToogleRow from 'Routes/Apps/Gluu/GluuToogleRow'
24-
import { getChangedFields, getMergedValues } from '@/helpers'
27+
import { useLoggingActions, type ModifiedFields } from './hooks/useLoggingActions'
28+
import { toast } from 'react-toastify'
2529

26-
function LoggingPage() {
30+
interface PendingValues {
31+
mergedValues: Logging
32+
changedFields: ModifiedFields
33+
}
34+
35+
function LoggingPage(): React.ReactElement {
2736
const { t } = useTranslation()
2837
const navigate = useNavigate()
2938
const { hasCedarPermission, authorize } = useCedarling()
30-
const logging = useSelector((state) => state.loggingReducer.logging)
31-
const loading = useSelector((state) => state.loggingReducer.loading)
32-
const { permissions: cedarPermissions } = useSelector((state) => state.cedarPermissions)
33-
34-
const dispatch = useDispatch()
39+
const { logLoggingUpdate } = useLoggingActions()
3540

3641
const [showCommitDialog, setShowCommitDialog] = useState(false)
37-
const [pendingValues, setPendingValues] = useState(null)
38-
const [localLogging, setLocalLogging] = useState(null)
42+
const [pendingValues, setPendingValues] = useState<PendingValues | null>(null)
43+
const [localLogging, setLocalLogging] = useState<Logging | null>(null)
44+
const [permissionsInitialized, setPermissionsInitialized] = useState(false)
45+
const [permissionError, setPermissionError] = useState(false)
46+
47+
const {
48+
data: logging,
49+
isLoading: isLoadingData,
50+
error: loggingError,
51+
} = useGetConfigLogging({
52+
query: {
53+
enabled: permissionsInitialized && hasCedarPermission(LOGGING_READ),
54+
},
55+
})
56+
const updateLogging = usePutConfigLogging()
3957

4058
useEffect(() => {
41-
const initPermissions = async () => {
42-
const permissions = [LOGGING_READ, LOGGING_WRITE]
43-
for (const permission of permissions) {
44-
await authorize([permission])
59+
let isMounted = true
60+
61+
const initPermissions = async (): Promise<void> => {
62+
const readResult = await authorize([LOGGING_READ])
63+
await authorize([LOGGING_WRITE])
64+
65+
if (!isMounted) return
66+
67+
if (!readResult.isAuthorized) {
68+
console.error('Failed to authorize READ permission:', readResult.error)
69+
setPermissionError(true)
4570
}
71+
72+
setPermissionsInitialized(true)
4673
}
74+
4775
initPermissions()
48-
dispatch(getLoggingConfig())
49-
}, [dispatch, authorize])
76+
77+
return () => {
78+
isMounted = false
79+
}
80+
}, [authorize])
5081

5182
useEffect(() => {
5283
if (logging) {
5384
setLocalLogging(logging)
5485
}
5586
}, [logging])
5687

57-
useEffect(() => {}, [cedarPermissions])
58-
59-
const initialValues = useMemo(() => getLoggingInitialValues(localLogging), [localLogging])
88+
const initialValues: LoggingFormValues = useMemo(
89+
() => getLoggingInitialValues(localLogging),
90+
[localLogging],
91+
)
6092

61-
const levels = useMemo(() => [...LOG_LEVELS], [])
62-
const logLayouts = useMemo(() => [...LOG_LAYOUTS], [])
93+
const levels = LOG_LEVELS
94+
const logLayouts = LOG_LAYOUTS
6395
SetTitle('Logging')
6496

6597
const handleSubmit = useCallback(
66-
(values) => {
98+
(values: LoggingFormValues): void => {
99+
if (!localLogging) {
100+
console.error('Cannot submit: logging data not loaded')
101+
return
102+
}
103+
67104
const mergedValues = getMergedValues(localLogging, values)
68105
const changedFields = getChangedFields(localLogging, mergedValues)
69106

107+
if (Object.keys(changedFields).length === 0) {
108+
return
109+
}
110+
70111
setPendingValues({ mergedValues, changedFields })
71112
setShowCommitDialog(true)
72113
},
73114
[localLogging],
74115
)
75116

76117
const handleAccept = useCallback(
77-
(userMessage) => {
78-
if (pendingValues) {
79-
const { mergedValues, changedFields } = pendingValues
80-
81-
const opts = {}
82-
opts['logging'] = JSON.stringify(mergedValues)
83-
84-
dispatch(
85-
editLoggingConfig({
86-
data: opts,
87-
otherFields: { userMessage, changedFields },
88-
}),
118+
async (userMessage: string): Promise<void> => {
119+
if (!pendingValues) return
120+
121+
const { mergedValues, changedFields } = pendingValues
122+
123+
try {
124+
const result = await updateLogging.mutateAsync({ data: mergedValues })
125+
126+
setLocalLogging(result)
127+
128+
logLoggingUpdate(userMessage, changedFields).catch((error) =>
129+
console.error('Audit logging failed:', error),
89130
)
131+
132+
toast.success(t('messages.success_in_saving'))
133+
90134
setShowCommitDialog(false)
91135
setPendingValues(null)
136+
} catch (error) {
137+
console.error('Failed to update logging configuration:', error)
138+
toast.error(t('messages.error_in_saving'))
92139
}
93140
},
94-
[pendingValues, dispatch],
141+
[pendingValues, updateLogging, logLoggingUpdate, t],
95142
)
96143

144+
const isLoading = !permissionsInitialized || isLoadingData || updateLogging.isPending
145+
146+
if (loggingError) {
147+
console.error('Failed to load logging configuration:', loggingError)
148+
return (
149+
<GluuLoader blocking={false}>
150+
<Card style={applicationStyle.mainCard}>
151+
<CardBody style={{ minHeight: 500 }}>
152+
<div className="alert alert-danger" role="alert">
153+
{t('messages.error_loading_logging')}
154+
</div>
155+
</CardBody>
156+
</Card>
157+
</GluuLoader>
158+
)
159+
}
160+
161+
if (permissionError) {
162+
return (
163+
<GluuLoader blocking={false}>
164+
<Card style={applicationStyle.mainCard}>
165+
<CardBody style={{ minHeight: 500 }}>
166+
<div className="alert alert-danger" role="alert">
167+
{t('messages.permission_error')}
168+
</div>
169+
</CardBody>
170+
</Card>
171+
</GluuLoader>
172+
)
173+
}
174+
97175
return (
98-
<GluuLoader blocking={loading}>
176+
<GluuLoader blocking={isLoading}>
99177
<Card style={applicationStyle.mainCard}>
100178
<CardBody style={{ minHeight: 500 }}>
101179
<GluuViewWrapper canShow={hasCedarPermission(LOGGING_READ)}>
@@ -121,7 +199,9 @@ function LoggingPage() {
121199
name="loggingLevel"
122200
data-testid="loggingLevel"
123201
value={formik.values.loggingLevel}
124-
onChange={(e) => formik.setFieldValue('loggingLevel', e.target.value)}
202+
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
203+
formik.setFieldValue('loggingLevel', e.target.value)
204+
}
125205
>
126206
<option value="">{t('actions.choose')}...</option>
127207
{levels.map((item, key) => (
@@ -147,7 +227,9 @@ function LoggingPage() {
147227
name="loggingLayout"
148228
data-testid="loggingLayout"
149229
value={formik.values.loggingLayout}
150-
onChange={(e) => formik.setFieldValue('loggingLayout', e.target.value)}
230+
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
231+
formik.setFieldValue('loggingLayout', e.target.value)
232+
}
151233
>
152234
<option value="">{t('actions.choose')}...</option>
153235
{logLayouts.map((item, key) => (
@@ -162,7 +244,9 @@ function LoggingPage() {
162244
<GluuToogleRow
163245
label="fields.http_logging_enabled"
164246
name="httpLoggingEnabled"
165-
handler={(e) => formik.setFieldValue('httpLoggingEnabled', e.target.checked)}
247+
handler={(e: React.ChangeEvent<HTMLInputElement>) =>
248+
formik.setFieldValue('httpLoggingEnabled', e.target.checked)
249+
}
166250
lsize={5}
167251
rsize={7}
168252
value={formik.values.httpLoggingEnabled}
@@ -171,7 +255,9 @@ function LoggingPage() {
171255
<GluuToogleRow
172256
label="fields.disable_jdk_logger"
173257
name="disableJdkLogger"
174-
handler={(e) => formik.setFieldValue('disableJdkLogger', e.target.checked)}
258+
handler={(e: React.ChangeEvent<HTMLInputElement>) =>
259+
formik.setFieldValue('disableJdkLogger', e.target.checked)
260+
}
175261
lsize={5}
176262
rsize={7}
177263
doc_category={JSON_CONFIG}
@@ -180,7 +266,7 @@ function LoggingPage() {
180266
<GluuToogleRow
181267
label="fields.enabled_oAuth_audit_logging"
182268
name="enabledOAuthAuditLogging"
183-
handler={(e) =>
269+
handler={(e: React.ChangeEvent<HTMLInputElement>) =>
184270
formik.setFieldValue('enabledOAuthAuditLogging', e.target.checked)
185271
}
186272
lsize={5}
@@ -206,7 +292,7 @@ function LoggingPage() {
206292
onApply={formik.handleSubmit}
207293
disableApply={!formik.isValid || !formik.dirty}
208294
applyButtonType="button"
209-
isLoading={loading}
295+
isLoading={isLoading}
210296
/>
211297
)}
212298
</Form>

0 commit comments

Comments
 (0)