Skip to content

Commit

Permalink
allow APIRootUrl, APIPathMode and APIPathStrip to be set in pre…
Browse files Browse the repository at this point in the history
…built (#176)
  • Loading branch information
samuelcolvin authored Feb 9, 2024
1 parent bb4fb36 commit 9c2ce0f
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 12 deletions.
8 changes: 7 additions & 1 deletion src/npm-fastui-prebuilt/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { FC, ReactNode } from 'react'
export default function App() {
return (
<FastUI
rootUrl="/api"
APIRootUrl={getMetaContent('fastui:APIRootUrl') || '/api'}
APIPathMode={getMetaContent('fastui:APIPathMode') as undefined | 'append' | 'query'}
APIPathStrip={getMetaContent('fastui:APIPathStrip')}
classNameGenerator={bootstrap.classNameGenerator}
customRender={customRender}
NotFound={NotFound}
Expand All @@ -15,6 +17,10 @@ export default function App() {
)
}

function getMetaContent(name: string): string | undefined {
return document.querySelector(`meta[name="${name}"]`)?.getAttribute('content') || undefined
}

const NotFound = ({ url }: { url: string }) => (
<div className="container mt-5 text-center">
<h1>Page not found</h1>
Expand Down
11 changes: 7 additions & 4 deletions src/npm-fastui/src/components/ServerLoad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,17 @@ const Render: FC<{ propsList: FastProps[] | null; notFoundUrl?: string; transiti
}

function useServerUrl(path: string): string {
const { rootUrl, pathSendMode } = useContext(ConfigContext)
const { APIRootUrl, APIPathMode, APIPathStrip } = useContext(ConfigContext)
if (APIPathStrip && path.startsWith(APIPathStrip)) {
path = path.slice(APIPathStrip.length)
}
const applyContext = useEventContext()
const requestPath = applyContext(path)

if (pathSendMode === 'query') {
return `${rootUrl}?path=${encodeURIComponent(requestPath)}`
if (APIPathMode === 'query') {
return `${APIRootUrl}?path=${encodeURIComponent(requestPath)}`
} else {
return rootUrl + requestPath
return APIRootUrl + requestPath
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/npm-fastui/src/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const DevReload: FC<{ enabled?: boolean }> = ({ enabled }) => {

const DevReloadActive = () => {
const { setError } = useContext(ErrorContext)
const { rootUrl } = useContext(ConfigContext)
const { APIRootUrl } = useContext(ConfigContext)

useEffect(() => {
let listening = true
Expand All @@ -36,7 +36,7 @@ const DevReloadActive = () => {
if (!listening || failCount >= 5) {
return count
}
const response = await fetch(rootUrl + '/__dev__/reload')
const response = await fetch(APIRootUrl + '/__dev__/reload')
count++
console.debug(`dev reload connected ${count}...`)
// if the response is okay, and we previously failed, clear error
Expand Down Expand Up @@ -78,6 +78,6 @@ const DevReloadActive = () => {
devConnected = false
}
}
}, [setError, rootUrl])
}, [setError, APIRootUrl])
return <></>
}
2 changes: 1 addition & 1 deletion src/npm-fastui/src/hooks/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { FastProps } from '../models'

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

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

export const useCustomRender = (props: FastProps): FC | void => {
const { customRender } = useContext(ConfigContext)
Expand Down
6 changes: 4 additions & 2 deletions src/npm-fastui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export { EventContextProvider } from './hooks/eventContext'
export type CustomRender = (props: FastProps) => FC | void

export interface FastUIProps {
rootUrl: string
APIRootUrl: string
// defaults to 'append'
pathSendMode?: 'append' | 'query'
APIPathMode?: 'append' | 'query'
// start of the path to remove from the URL before making a request to the API
APIPathStrip?: string
Spinner?: FC
NotFound?: FC<{ url: string }>
Transition?: FC<{ children: ReactNode; transitioning: boolean }>
Expand Down
21 changes: 20 additions & 1 deletion src/python-fastui/fastui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,34 @@ def coerce_to_list(cls, v):
_PREBUILT_CDN_URL = f'https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt@{_PREBUILT_VERSION}/dist/assets'


def prebuilt_html(title: str = ''):
def prebuilt_html(
*,
title: str = '',
api_root_url: _t.Union[str, None] = None,
api_path_mode: _t.Union[_t.Literal['append', 'query'], None] = None,
api_path_strip: _t.Union[str, None] = None,
) -> str:
"""
Returns a simple HTML page which includes the FastUI react frontend, loaded from https://www.jsdelivr.com/.
Arguments:
title: page title
api_root_url: the root URL of the API backend, which will be used to get data, default is '/api'.
api_path_mode: whether to append the page path to the root API request URL, or use it as a query parameter,
default is 'append'.
api_path_strip: string to remove from the start of the page path before making the API request.
Returns:
HTML string which can be returned by an endpoint to serve the FastUI frontend.
"""
meta_extra = []
if api_root_url is not None:
meta_extra.append(f'<meta name="fastui:APIRootUrl" content="{api_root_url}" />')
if api_path_mode is not None:
meta_extra.append(f'<meta name="fastui:APIPathMode" content="{api_path_mode}" />')
if api_path_strip is not None:
meta_extra.append(f'<meta name="fastui:APIPathStrip" content="{api_path_strip}" />')
meta_extra_str = '\n '.join(meta_extra)
# language=HTML
return f"""\
<!doctype html>
Expand All @@ -47,6 +65,7 @@ def prebuilt_html(title: str = ''):
<title>{title}</title>
<script type="module" crossorigin src="{_PREBUILT_CDN_URL}/index.js"></script>
<link rel="stylesheet" crossorigin href="{_PREBUILT_CDN_URL}/index.css">
{meta_extra_str}
</head>
<body>
<div id="root"></div>
Expand Down
24 changes: 24 additions & 0 deletions src/python-fastui/tests/test_prebuilt_html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from fastui import prebuilt_html


def test_prebuilt_html():
html = prebuilt_html()
assert html.startswith('<!doctype html>')
assert 'https://cdn.jsdelivr.net/npm/@pydantic/fastui-prebuilt' in html
assert '<title></title>' in html
assert '<meta name="fastui:APIRootUrl"' not in html
assert '<meta name="fastui:APIPathMode"' not in html
assert '<meta name="fastui:APIPathStrip"' not in html


def test_prebuilt_html_meta_tags():
html = prebuilt_html(
title='Test Title',
api_root_url='/admin/api',
api_path_mode='query',
api_path_strip='/admin',
)
assert '<title>Test Title</title>' in html
assert '<meta name="fastui:APIRootUrl" content="/admin/api" />' in html
assert '<meta name="fastui:APIPathMode" content="query" />' in html
assert '<meta name="fastui:APIPathStrip" content="/admin" />' in html

0 comments on commit 9c2ce0f

Please sign in to comment.