Skip to content

Commit 9c2ce0f

Browse files
authored
allow APIRootUrl, APIPathMode and APIPathStrip to be set in prebuilt (#176)
1 parent bb4fb36 commit 9c2ce0f

File tree

7 files changed

+66
-12
lines changed

7 files changed

+66
-12
lines changed

src/npm-fastui-prebuilt/src/App.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { FC, ReactNode } from 'react'
55
export default function App() {
66
return (
77
<FastUI
8-
rootUrl="/api"
8+
APIRootUrl={getMetaContent('fastui:APIRootUrl') || '/api'}
9+
APIPathMode={getMetaContent('fastui:APIPathMode') as undefined | 'append' | 'query'}
10+
APIPathStrip={getMetaContent('fastui:APIPathStrip')}
911
classNameGenerator={bootstrap.classNameGenerator}
1012
customRender={customRender}
1113
NotFound={NotFound}
@@ -15,6 +17,10 @@ export default function App() {
1517
)
1618
}
1719

20+
function getMetaContent(name: string): string | undefined {
21+
return document.querySelector(`meta[name="${name}"]`)?.getAttribute('content') || undefined
22+
}
23+
1824
const NotFound = ({ url }: { url: string }) => (
1925
<div className="container mt-5 text-center">
2026
<h1>Page not found</h1>

src/npm-fastui/src/components/ServerLoad.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,17 @@ const Render: FC<{ propsList: FastProps[] | null; notFoundUrl?: string; transiti
123123
}
124124

125125
function useServerUrl(path: string): string {
126-
const { rootUrl, pathSendMode } = useContext(ConfigContext)
126+
const { APIRootUrl, APIPathMode, APIPathStrip } = useContext(ConfigContext)
127+
if (APIPathStrip && path.startsWith(APIPathStrip)) {
128+
path = path.slice(APIPathStrip.length)
129+
}
127130
const applyContext = useEventContext()
128131
const requestPath = applyContext(path)
129132

130-
if (pathSendMode === 'query') {
131-
return `${rootUrl}?path=${encodeURIComponent(requestPath)}`
133+
if (APIPathMode === 'query') {
134+
return `${APIRootUrl}?path=${encodeURIComponent(requestPath)}`
132135
} else {
133-
return rootUrl + requestPath
136+
return APIRootUrl + requestPath
134137
}
135138
}
136139

src/npm-fastui/src/dev.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const DevReload: FC<{ enabled?: boolean }> = ({ enabled }) => {
2121

2222
const DevReloadActive = () => {
2323
const { setError } = useContext(ErrorContext)
24-
const { rootUrl } = useContext(ConfigContext)
24+
const { APIRootUrl } = useContext(ConfigContext)
2525

2626
useEffect(() => {
2727
let listening = true
@@ -36,7 +36,7 @@ const DevReloadActive = () => {
3636
if (!listening || failCount >= 5) {
3737
return count
3838
}
39-
const response = await fetch(rootUrl + '/__dev__/reload')
39+
const response = await fetch(APIRootUrl + '/__dev__/reload')
4040
count++
4141
console.debug(`dev reload connected ${count}...`)
4242
// if the response is okay, and we previously failed, clear error
@@ -78,6 +78,6 @@ const DevReloadActive = () => {
7878
devConnected = false
7979
}
8080
}
81-
}, [setError, rootUrl])
81+
}, [setError, APIRootUrl])
8282
return <></>
8383
}

src/npm-fastui/src/hooks/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { FastProps } from '../models'
55

66
type Config = Omit<FastUIProps, 'DisplayError' | 'classNameGenerator' | 'devMode'>
77

8-
export const ConfigContext = createContext<Config>({ rootUrl: '' })
8+
export const ConfigContext = createContext<Config>({ APIRootUrl: '' })
99

1010
export const useCustomRender = (props: FastProps): FC | void => {
1111
const { customRender } = useContext(ConfigContext)

src/npm-fastui/src/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ export { EventContextProvider } from './hooks/eventContext'
1919
export type CustomRender = (props: FastProps) => FC | void
2020

2121
export interface FastUIProps {
22-
rootUrl: string
22+
APIRootUrl: string
2323
// defaults to 'append'
24-
pathSendMode?: 'append' | 'query'
24+
APIPathMode?: 'append' | 'query'
25+
// start of the path to remove from the URL before making a request to the API
26+
APIPathStrip?: string
2527
Spinner?: FC
2628
NotFound?: FC<{ url: string }>
2729
Transition?: FC<{ children: ReactNode; transitioning: boolean }>

src/python-fastui/fastui/__init__.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,34 @@ def coerce_to_list(cls, v):
2727
_PREBUILT_CDN_URL = f'https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt@{_PREBUILT_VERSION}/dist/assets'
2828

2929

30-
def prebuilt_html(title: str = ''):
30+
def prebuilt_html(
31+
*,
32+
title: str = '',
33+
api_root_url: _t.Union[str, None] = None,
34+
api_path_mode: _t.Union[_t.Literal['append', 'query'], None] = None,
35+
api_path_strip: _t.Union[str, None] = None,
36+
) -> str:
3137
"""
3238
Returns a simple HTML page which includes the FastUI react frontend, loaded from https://www.jsdelivr.com/.
3339
3440
Arguments:
3541
title: page title
42+
api_root_url: the root URL of the API backend, which will be used to get data, default is '/api'.
43+
api_path_mode: whether to append the page path to the root API request URL, or use it as a query parameter,
44+
default is 'append'.
45+
api_path_strip: string to remove from the start of the page path before making the API request.
3646
3747
Returns:
3848
HTML string which can be returned by an endpoint to serve the FastUI frontend.
3949
"""
50+
meta_extra = []
51+
if api_root_url is not None:
52+
meta_extra.append(f'<meta name="fastui:APIRootUrl" content="{api_root_url}" />')
53+
if api_path_mode is not None:
54+
meta_extra.append(f'<meta name="fastui:APIPathMode" content="{api_path_mode}" />')
55+
if api_path_strip is not None:
56+
meta_extra.append(f'<meta name="fastui:APIPathStrip" content="{api_path_strip}" />')
57+
meta_extra_str = '\n '.join(meta_extra)
4058
# language=HTML
4159
return f"""\
4260
<!doctype html>
@@ -47,6 +65,7 @@ def prebuilt_html(title: str = ''):
4765
<title>{title}</title>
4866
<script type="module" crossorigin src="{_PREBUILT_CDN_URL}/index.js"></script>
4967
<link rel="stylesheet" crossorigin href="{_PREBUILT_CDN_URL}/index.css">
68+
{meta_extra_str}
5069
</head>
5170
<body>
5271
<div id="root"></div>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from fastui import prebuilt_html
2+
3+
4+
def test_prebuilt_html():
5+
html = prebuilt_html()
6+
assert html.startswith('<!doctype html>')
7+
assert 'https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt' in html
8+
assert '<title></title>' in html
9+
assert '<meta name="fastui:APIRootUrl"' not in html
10+
assert '<meta name="fastui:APIPathMode"' not in html
11+
assert '<meta name="fastui:APIPathStrip"' not in html
12+
13+
14+
def test_prebuilt_html_meta_tags():
15+
html = prebuilt_html(
16+
title='Test Title',
17+
api_root_url='/admin/api',
18+
api_path_mode='query',
19+
api_path_strip='/admin',
20+
)
21+
assert '<title>Test Title</title>' in html
22+
assert '<meta name="fastui:APIRootUrl" content="/admin/api" />' in html
23+
assert '<meta name="fastui:APIPathMode" content="query" />' in html
24+
assert '<meta name="fastui:APIPathStrip" content="/admin" />' in html

0 commit comments

Comments
 (0)