Skip to content

Commit 4d8342e

Browse files
authored
feat(admin-ui): Add Orval Support for Progressive Migration from OpenAPI Generator (#2300)
* feat(admin-ui): Add Orval Support for Progressive Migration from OpenAPI Generator #2299 * feat(admin-ui): fix wrong enums and update orval config #2299 * feat(admin-ui): apply reviews suggestion, also use reactquery instead of axios #2299 * feat(admin-ui): update configuration based on the real testing feedbacks #2299
1 parent 58313fc commit 4d8342e

File tree

6 files changed

+141
-8
lines changed

6 files changed

+141
-8
lines changed

admin-ui/api-client.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Axios, { AxiosRequestConfig, AxiosHeaders } from 'axios'
2+
import store from './app/redux/store'
3+
4+
const baseUrl =
5+
(typeof window !== 'undefined' && (window as any).configApiBaseUrl) ||
6+
process.env.CONFIG_API_BASE_URL ||
7+
''
8+
9+
export const AXIOS_INSTANCE = Axios.create({ baseURL: baseUrl, timeout: 60000 })
10+
11+
AXIOS_INSTANCE.interceptors.request.use((config) => {
12+
// Get token and issuer from Redux store instead of localStorage
13+
const state = store.getState()
14+
const authState = (state as any)?.authReducer
15+
const token = authState?.token?.access_token || null
16+
const issuer = authState?.issuer || null
17+
const userInum = authState?.userInum || ''
18+
19+
if ((config.headers as any) && typeof (config.headers as any).set === 'function') {
20+
const headers = config.headers as unknown as AxiosHeaders
21+
if (token) headers.set('Authorization', `Bearer ${token}`)
22+
if (issuer) headers.set('issuer', issuer)
23+
headers.set('jans-client', 'admin-ui')
24+
if (userInum) headers.set('User-inum', userInum)
25+
} else {
26+
config.headers = {
27+
...(config.headers as any),
28+
...(token ? { Authorization: `Bearer ${token}` } : {}),
29+
...(issuer ? { issuer } : {}),
30+
'jans-client': 'admin-ui',
31+
...(userInum ? { 'User-inum': userInum } : {}),
32+
} as any
33+
}
34+
return config
35+
})
36+
37+
// React Query compatible instance
38+
export const customInstance = <T>(
39+
config: AxiosRequestConfig,
40+
options?: { signal?: AbortSignal },
41+
): Promise<T> => {
42+
const source = Axios.CancelToken.source()
43+
44+
const promise = AXIOS_INSTANCE({
45+
...config,
46+
cancelToken: source.token,
47+
signal: options?.signal,
48+
}).then(({ data }) => data)
49+
50+
// For React Query compatibility, we attach the cancel method to the promise
51+
;(promise as any).cancel = () => {
52+
source.cancel('Operation canceled by the user.')
53+
}
54+
55+
return promise
56+
}

admin-ui/app/index.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,33 @@ import App from './components/App'
33
import { I18nextProvider } from 'react-i18next'
44
import i18n from './i18n'
55
import { ThemeProvider } from 'Context/theme/themeContext'
6+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
7+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
68
import './styles/index.css'
79
import 'bootstrap/dist/css/bootstrap.css'
810

11+
// Create a client
12+
const queryClient = new QueryClient({
13+
defaultOptions: {
14+
queries: {
15+
retry: 3,
16+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
17+
staleTime: 5 * 60 * 1000, // 5 minutes
18+
refetchOnWindowFocus: false,
19+
},
20+
},
21+
})
22+
923
const container = document.querySelector('#root') as HTMLElement
1024
const root = createRoot(container)
1125

1226
root.render(
13-
<I18nextProvider i18n={i18n}>
14-
<ThemeProvider>
15-
<App />
16-
</ThemeProvider>
17-
</I18nextProvider>,
27+
<QueryClientProvider client={queryClient}>
28+
<I18nextProvider i18n={i18n}>
29+
<ThemeProvider>
30+
<App />
31+
</ThemeProvider>
32+
</I18nextProvider>
33+
<ReactQueryDevtools initialIsOpen={false} />
34+
</QueryClientProvider>,
1835
)

admin-ui/fix-orval-enums.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env node
2+
const fs = require('fs')
3+
const path = require('path')
4+
const filePath = path.join(__dirname, 'jans_config_api_orval', 'src', 'JansConfigApi.ts')
5+
6+
if (!fs.existsSync(filePath)) {
7+
console.log('JansConfigApi.ts not found, skipping enum fixes')
8+
process.exit(0)
9+
}
10+
11+
let content = fs.readFileSync(filePath, 'utf8')
12+
13+
// Fix malformed enum keys with unescaped single quotes
14+
// Pattern: 'KeyOps{value=\'text'} -> 'KeyOps{value=\'text\'}
15+
const finalContent = content.replace(
16+
/('KeyOps\{value=\\')([^'\\]+)(')/g,
17+
(match, prefix, value, suffix) => {
18+
return `${prefix}${value}\\${suffix}`
19+
},
20+
)
21+
22+
if (finalContent !== content) {
23+
fs.writeFileSync(filePath, finalContent)
24+
console.log('✅ Fixed malformed enum keys in JansConfigApi.ts')
25+
} else {
26+
console.log('✅ No enum fixes needed in JansConfigApi.ts')
27+
}

admin-ui/orval.config.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { defineConfig } from 'orval'
2+
3+
export default defineConfig({
4+
jans: {
5+
input: {
6+
target: './configApiSpecs.yaml',
7+
},
8+
output: {
9+
mode: 'single',
10+
target: './jans_config_api_orval/src/JansConfigApi.ts',
11+
client: 'react-query',
12+
httpClient: 'axios',
13+
mock: false,
14+
prettier: true,
15+
override: {
16+
mutator: {
17+
path: './api-client.ts',
18+
name: 'customInstance',
19+
},
20+
query: {
21+
useQuery: true,
22+
useMutation: true,
23+
useInfinite: true,
24+
version: 5,
25+
},
26+
},
27+
},
28+
},
29+
})

admin-ui/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
"lint": "eslint app/ plugins/ --ext .js,.jsx,.ts,.tsx --fix",
3636
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json}\"",
3737
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json}\"",
38-
"api": "rm -rf jans_config_api && openapi-merge-cli && openapi-generator-cli generate -i ./configApiSpecs.yaml --global-property skipFormModel=false -g javascript -o jans_config_api && cd jans_config_api && npm i mocha@10.2.0 && npm install && npm run build",
39-
"api-win": "del /f jans_config_api && openapi-merge-cli && openapi-generator-cli generate -i ./configApiSpecs.yaml --global-property skipFormModel=false -g javascript -o jans_config_api && cd jans_config_api && npm i mocha@10.2.0 && npm install && npm run build",
38+
"api": "rimraf jans_config_api && openapi-merge-cli && openapi-generator-cli generate -i ./configApiSpecs.yaml --global-property skipFormModel=false -g javascript -o jans_config_api && cd jans_config_api && npm i mocha@10.2.0 && npm install && npm run build",
39+
"api:orval": "rimraf jans_config_api_orval && openapi-merge-cli && orval --config ./orval.config.ts && node fix-orval-enums.js",
4040
"plugin:showAll": "node ./build/plugin-cli.js --showAllPlugins",
4141
"plugin:add": "node ./build/plugin-cli.js --addPlugin",
4242
"plugin:remove": "node ./build/plugin-cli.js --removePlugin",
@@ -104,6 +104,7 @@
104104
"lint-staged": "^16.0.0",
105105
"mini-css-extract-plugin": "^2.7.5",
106106
"mkdirp": "^2.1.6",
107+
"orval": "^7.11.2",
107108
"postcss-loader": "^7.2.4",
108109
"prettier": "^3.5.3",
109110
"purgecss-webpack-plugin": "^5.0.0",
@@ -140,6 +141,8 @@
140141
"@mui/x-date-pickers": "^7.0.0",
141142
"@openid/appauth": "^1.3.1",
142143
"@reduxjs/toolkit": "^2.2.1",
144+
"@tanstack/react-query": "^5.90.2",
145+
"@tanstack/react-query-devtools": "^5.90.2",
143146
"ace-builds": "^1.16.0",
144147
"animejs": "^3.2.1",
145148
"axios": "^1.8.1",

admin-ui/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"Redux/*": ["app/redux/*"],
2727
"Images/*": ["app/images/*"],
2828
"Contexts/*": ["app/contexts/*"],
29-
"SVG/*": ["app/components/SVG/*"]
29+
"SVG/*": ["app/components/SVG/*"],
30+
"JansConfigApi": ["jans_config_api_orval/src/JansConfigApi.ts"]
3031
},
3132
"plugins": [
3233
{

0 commit comments

Comments
 (0)