Extract HTML5 setup controller
This commit is contained in:
@@ -0,0 +1,163 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from collections.abc import AsyncIterator, Callable, Iterable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
from api_server.html5_forms import form_value
|
||||||
|
from api_server.html5_setup import (
|
||||||
|
render_html5_import_check,
|
||||||
|
render_html5_import_job,
|
||||||
|
render_html5_project_setup,
|
||||||
|
render_html5_settings_panel,
|
||||||
|
render_html5_setup_summary,
|
||||||
|
)
|
||||||
|
from api_server.html5_sse import html5_sse_comment, html5_sse_if_changed
|
||||||
|
|
||||||
|
|
||||||
|
def html5_setup_page(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
projects: Iterable[object],
|
||||||
|
setup: object,
|
||||||
|
) -> str:
|
||||||
|
return render_html5_project_setup(project_id=project_id, projects=projects, setup=setup)
|
||||||
|
|
||||||
|
|
||||||
|
def html5_setup_summary(*, project_id: str, setup: object) -> str:
|
||||||
|
return render_html5_setup_summary(project_id, setup)
|
||||||
|
|
||||||
|
|
||||||
|
async def html5_setup_event_stream(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
setup_response: Callable[[str], object],
|
||||||
|
latest_import_job: Callable[[str], object | None],
|
||||||
|
once: bool = False,
|
||||||
|
) -> AsyncIterator[str]:
|
||||||
|
last_fragments: dict[str, str] = {}
|
||||||
|
while True:
|
||||||
|
yield html5_sse_comment(f"setup {project_id} heartbeat")
|
||||||
|
try:
|
||||||
|
setup = setup_response(project_id)
|
||||||
|
except HTTPException as error:
|
||||||
|
setup_error = f'<div class="setup-summary" data-html5-setup-summary><p class="muted padded">{error.detail}</p></div>'
|
||||||
|
for event_text in html5_sse_if_changed(last_fragments, "setup-summary", setup_error):
|
||||||
|
yield event_text
|
||||||
|
if once:
|
||||||
|
break
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
continue
|
||||||
|
for event_text in html5_sse_if_changed(last_fragments, "setup-summary", render_html5_setup_summary(project_id, setup)):
|
||||||
|
yield event_text
|
||||||
|
for event_text in html5_sse_if_changed(
|
||||||
|
last_fragments,
|
||||||
|
"setup-import-job",
|
||||||
|
render_html5_import_job(project_id, latest_import_job(project_id)),
|
||||||
|
):
|
||||||
|
yield event_text
|
||||||
|
if once:
|
||||||
|
break
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
async def html5_setup_source(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
form: dict[str, list[str]],
|
||||||
|
import_source_kind: Callable[[str], Any],
|
||||||
|
setup_response: Callable[[str], object],
|
||||||
|
save_settings: Callable[[str, object], Any],
|
||||||
|
) -> str:
|
||||||
|
source = import_source_kind(form_value(form, "source") or "XML_DUMP")
|
||||||
|
current = setup_response(project_id)
|
||||||
|
settings = current.settings.model_copy(update={"structure_source": source})
|
||||||
|
setup = await save_settings(project_id, settings)
|
||||||
|
return render_html5_setup_summary(project_id, setup)
|
||||||
|
|
||||||
|
|
||||||
|
async def html5_setup_settings(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
form: dict[str, list[str]],
|
||||||
|
setup_response: Callable[[str], object],
|
||||||
|
save_settings: Callable[[str, object], Any],
|
||||||
|
) -> str:
|
||||||
|
current = setup_response(project_id)
|
||||||
|
settings = current.settings.model_copy(
|
||||||
|
update={
|
||||||
|
"name": form_value(form, "name") or current.settings.name,
|
||||||
|
"platform_version": form_value(form, "platform_version"),
|
||||||
|
"compatibility_mode": form_value(form, "compatibility_mode"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
setup = await save_settings(project_id, settings)
|
||||||
|
return render_html5_settings_panel(project_id, setup, saved=True)
|
||||||
|
|
||||||
|
|
||||||
|
def html5_setup_check(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
form: dict[str, list[str]],
|
||||||
|
import_source_kind: Callable[[str], Any],
|
||||||
|
import_request: Callable[..., object],
|
||||||
|
current_import_source: Callable[[str], object],
|
||||||
|
import_check: Callable[[str, object, object], object],
|
||||||
|
) -> str:
|
||||||
|
source = import_source_kind(form_value(form, "source") or current_import_source(project_id).value)
|
||||||
|
check = import_check(project_id, source, import_request(source=source))
|
||||||
|
return render_html5_import_check(project_id, check)
|
||||||
|
|
||||||
|
|
||||||
|
def html5_setup_import(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
form: dict[str, list[str]],
|
||||||
|
import_source_kind: Callable[[str], Any],
|
||||||
|
import_request: Callable[..., object],
|
||||||
|
current_import_source: Callable[[str], object],
|
||||||
|
execute_import: Callable[[str, object], object],
|
||||||
|
setup_response: Callable[[str], object],
|
||||||
|
) -> str:
|
||||||
|
source = import_source_kind(form_value(form, "source") or current_import_source(project_id).value)
|
||||||
|
structure_only = form_value(form, "structure_only") in {"1", "true", "on", "yes"}
|
||||||
|
execute_import(project_id, import_request(source=source, structure_only=structure_only))
|
||||||
|
return render_html5_setup_summary(project_id, setup_response(project_id))
|
||||||
|
|
||||||
|
|
||||||
|
async def html5_setup_import_job(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
form: dict[str, list[str]],
|
||||||
|
import_source_kind: Callable[[str], Any],
|
||||||
|
import_request: Callable[..., object],
|
||||||
|
current_import_source: Callable[[str], object],
|
||||||
|
start_import_job: Callable[[str, object, object], Any],
|
||||||
|
) -> str:
|
||||||
|
source = import_source_kind(form_value(form, "source") or current_import_source(project_id).value)
|
||||||
|
job = await start_import_job(project_id, source, import_request(source=source))
|
||||||
|
return render_html5_import_job(project_id, job)
|
||||||
|
|
||||||
|
|
||||||
|
def html5_setup_job(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
job_id: str,
|
||||||
|
jobs_by_id: dict[str, object],
|
||||||
|
) -> str:
|
||||||
|
job = jobs_by_id.get(job_id)
|
||||||
|
if job is None or (getattr(job, "payload", {}) or {}).get("project_id") != project_id:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Unknown import job: {job_id}")
|
||||||
|
return render_html5_import_job(project_id, job)
|
||||||
|
|
||||||
|
|
||||||
|
async def html5_setup_reindex(
|
||||||
|
*,
|
||||||
|
project_id: str,
|
||||||
|
reindex: Callable[[str], Any],
|
||||||
|
setup_response: Callable[[str], object],
|
||||||
|
) -> str:
|
||||||
|
await reindex(project_id)
|
||||||
|
return render_html5_setup_summary(project_id, setup_response(project_id))
|
||||||
@@ -90,12 +90,17 @@ from api_server.html5_operations_controller import (
|
|||||||
html5_operations_event_stream as _html5_operations_event_stream,
|
html5_operations_event_stream as _html5_operations_event_stream,
|
||||||
html5_operations_page as _html5_operations_page,
|
html5_operations_page as _html5_operations_page,
|
||||||
)
|
)
|
||||||
from api_server.html5_setup import (
|
from api_server.html5_setup_controller import (
|
||||||
render_html5_import_check,
|
html5_setup_check as _html5_setup_check,
|
||||||
render_html5_import_job,
|
html5_setup_event_stream as _html5_setup_event_stream,
|
||||||
render_html5_project_setup,
|
html5_setup_import as _html5_setup_import,
|
||||||
render_html5_settings_panel,
|
html5_setup_import_job as _html5_setup_import_job,
|
||||||
render_html5_setup_summary,
|
html5_setup_job as _html5_setup_job,
|
||||||
|
html5_setup_page as _html5_setup_page,
|
||||||
|
html5_setup_reindex as _html5_setup_reindex,
|
||||||
|
html5_setup_settings as _html5_setup_settings,
|
||||||
|
html5_setup_source as _html5_setup_source,
|
||||||
|
html5_setup_summary as _html5_setup_summary,
|
||||||
)
|
)
|
||||||
from impact_engine import object_impact, routine_impact
|
from impact_engine import object_impact, routine_impact
|
||||||
from incremental_indexer import rebuild_changed_file
|
from incremental_indexer import rebuild_changed_file
|
||||||
@@ -2019,113 +2024,115 @@ async def html5_project_authoring_apply_metadata_object(project_id: str, request
|
|||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/setup")
|
@app.get("/html5/projects/{project_id}/setup")
|
||||||
async def html5_project_setup(project_id: str) -> Response:
|
async def html5_project_setup(project_id: str) -> Response:
|
||||||
setup = _project_setup_response(project_id)
|
|
||||||
return _html5_response(
|
return _html5_response(
|
||||||
render_html5_project_setup(project_id=project_id, projects=_project_summaries(), setup=setup),
|
_html5_setup_page(project_id=project_id, projects=_project_summaries(), setup=_project_setup_response(project_id)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/setup/summary")
|
@app.get("/html5/projects/{project_id}/setup/summary")
|
||||||
async def html5_project_setup_summary(project_id: str) -> Response:
|
async def html5_project_setup_summary(project_id: str) -> Response:
|
||||||
setup = _project_setup_response(project_id)
|
return _html5_response(_html5_setup_summary(project_id=project_id, setup=_project_setup_response(project_id)))
|
||||||
return _html5_response(render_html5_setup_summary(project_id, setup))
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/setup/events")
|
@app.get("/html5/projects/{project_id}/setup/events")
|
||||||
async def html5_project_setup_events(project_id: str, once: bool = False) -> StreamingResponse:
|
async def html5_project_setup_events(project_id: str, once: bool = False) -> StreamingResponse:
|
||||||
async def stream_setup():
|
return _html5_sse_response(
|
||||||
last_fragments: dict[str, str] = {}
|
_html5_setup_event_stream(
|
||||||
while True:
|
project_id=project_id,
|
||||||
yield _html5_sse_comment(f"setup {project_id} heartbeat")
|
setup_response=_project_setup_response,
|
||||||
try:
|
latest_import_job=_html5_latest_import_job,
|
||||||
setup = _project_setup_response(project_id)
|
once=once,
|
||||||
except HTTPException as error:
|
)
|
||||||
setup_error = f'<div class="setup-summary" data-html5-setup-summary><p class="muted padded">{error.detail}</p></div>'
|
)
|
||||||
for event_text in _html5_sse_if_changed(last_fragments, "setup-summary", setup_error):
|
|
||||||
yield event_text
|
|
||||||
if once:
|
|
||||||
break
|
|
||||||
await asyncio.sleep(2)
|
|
||||||
continue
|
|
||||||
for event_text in _html5_sse_if_changed(last_fragments, "setup-summary", render_html5_setup_summary(project_id, setup)):
|
|
||||||
yield event_text
|
|
||||||
for event_text in _html5_sse_if_changed(
|
|
||||||
last_fragments,
|
|
||||||
"setup-import-job",
|
|
||||||
render_html5_import_job(project_id, _html5_latest_import_job(project_id)),
|
|
||||||
):
|
|
||||||
yield event_text
|
|
||||||
if once:
|
|
||||||
break
|
|
||||||
await asyncio.sleep(2)
|
|
||||||
|
|
||||||
return _html5_sse_response(stream_setup())
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/source")
|
@app.post("/html5/projects/{project_id}/setup/source")
|
||||||
async def html5_project_setup_source(project_id: str, request: Request) -> Response:
|
async def html5_project_setup_source(project_id: str, request: Request) -> Response:
|
||||||
form = await _html5_form_data(request)
|
form = await _html5_form_data(request)
|
||||||
source = ImportSourceKind(_form_value(form, "source") or ImportSourceKind.XML_DUMP.value)
|
return _html5_response(
|
||||||
current = _project_setup_response(project_id)
|
await _html5_setup_source(
|
||||||
settings = current.settings.model_copy(update={"structure_source": source})
|
project_id=project_id,
|
||||||
setup = await save_project_settings(project_id, settings)
|
form=form,
|
||||||
return _html5_response(render_html5_setup_summary(project_id, setup))
|
import_source_kind=ImportSourceKind,
|
||||||
|
setup_response=_project_setup_response,
|
||||||
|
save_settings=save_project_settings,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/settings")
|
@app.post("/html5/projects/{project_id}/setup/settings")
|
||||||
async def html5_project_setup_settings(project_id: str, request: Request) -> Response:
|
async def html5_project_setup_settings(project_id: str, request: Request) -> Response:
|
||||||
form = await _html5_form_data(request)
|
form = await _html5_form_data(request)
|
||||||
current = _project_setup_response(project_id)
|
return _html5_response(
|
||||||
settings = current.settings.model_copy(
|
await _html5_setup_settings(
|
||||||
update={
|
project_id=project_id,
|
||||||
"name": _form_value(form, "name") or current.settings.name,
|
form=form,
|
||||||
"platform_version": _form_value(form, "platform_version"),
|
setup_response=_project_setup_response,
|
||||||
"compatibility_mode": _form_value(form, "compatibility_mode"),
|
save_settings=save_project_settings,
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
setup = await save_project_settings(project_id, settings)
|
|
||||||
return _html5_response(render_html5_settings_panel(project_id, setup, saved=True))
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/check")
|
@app.post("/html5/projects/{project_id}/setup/check")
|
||||||
async def html5_project_setup_check(project_id: str, request: Request) -> Response:
|
async def html5_project_setup_check(project_id: str, request: Request) -> Response:
|
||||||
form = await _html5_form_data(request)
|
form = await _html5_form_data(request)
|
||||||
source = ImportSourceKind(_form_value(form, "source") or _current_import_source(project_id).value)
|
return _html5_response(
|
||||||
check = _import_check_response(project_id, source, ImportRequest(source=source))
|
_html5_setup_check(
|
||||||
return _html5_response(render_html5_import_check(project_id, check))
|
project_id=project_id,
|
||||||
|
form=form,
|
||||||
|
import_source_kind=ImportSourceKind,
|
||||||
|
import_request=ImportRequest,
|
||||||
|
current_import_source=_current_import_source,
|
||||||
|
import_check=_import_check_response,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/import")
|
@app.post("/html5/projects/{project_id}/setup/import")
|
||||||
async def html5_project_setup_import(project_id: str, request: Request) -> Response:
|
async def html5_project_setup_import(project_id: str, request: Request) -> Response:
|
||||||
form = await _html5_form_data(request)
|
form = await _html5_form_data(request)
|
||||||
source = ImportSourceKind(_form_value(form, "source") or _current_import_source(project_id).value)
|
return _html5_response(
|
||||||
structure_only = _form_value(form, "structure_only") in {"1", "true", "on", "yes"}
|
_html5_setup_import(
|
||||||
_execute_import_project(project_id, ImportRequest(source=source, structure_only=structure_only))
|
project_id=project_id,
|
||||||
setup = _project_setup_response(project_id)
|
form=form,
|
||||||
return _html5_response(render_html5_setup_summary(project_id, setup))
|
import_source_kind=ImportSourceKind,
|
||||||
|
import_request=ImportRequest,
|
||||||
|
current_import_source=_current_import_source,
|
||||||
|
execute_import=_execute_import_project,
|
||||||
|
setup_response=_project_setup_response,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/import-job")
|
@app.post("/html5/projects/{project_id}/setup/import-job")
|
||||||
async def html5_project_setup_import_job(project_id: str, request: Request) -> Response:
|
async def html5_project_setup_import_job(project_id: str, request: Request) -> Response:
|
||||||
form = await _html5_form_data(request)
|
form = await _html5_form_data(request)
|
||||||
source = ImportSourceKind(_form_value(form, "source") or _current_import_source(project_id).value)
|
return _html5_response(
|
||||||
job = await start_project_import_job(project_id, source, ImportRequest(source=source))
|
await _html5_setup_import_job(
|
||||||
return _html5_response(render_html5_import_job(project_id, job))
|
project_id=project_id,
|
||||||
|
form=form,
|
||||||
|
import_source_kind=ImportSourceKind,
|
||||||
|
import_request=ImportRequest,
|
||||||
|
current_import_source=_current_import_source,
|
||||||
|
start_import_job=start_project_import_job,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/setup/jobs/{job_id}")
|
@app.get("/html5/projects/{project_id}/setup/jobs/{job_id}")
|
||||||
async def html5_project_setup_job(project_id: str, job_id: str) -> Response:
|
async def html5_project_setup_job(project_id: str, job_id: str) -> Response:
|
||||||
job = _operations.jobs.get(job_id)
|
return _html5_response(_html5_setup_job(project_id=project_id, job_id=job_id, jobs_by_id=_operations.jobs))
|
||||||
if job is None or job.payload.get("project_id") != project_id:
|
|
||||||
raise HTTPException(status_code=404, detail=f"Unknown import job: {job_id}")
|
|
||||||
return _html5_response(render_html5_import_job(project_id, job))
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/html5/projects/{project_id}/setup/reindex")
|
@app.post("/html5/projects/{project_id}/setup/reindex")
|
||||||
async def html5_project_setup_reindex(project_id: str) -> Response:
|
async def html5_project_setup_reindex(project_id: str) -> Response:
|
||||||
await reindex_project(project_id)
|
return _html5_response(
|
||||||
setup = _project_setup_response(project_id)
|
await _html5_setup_reindex(
|
||||||
return _html5_response(render_html5_setup_summary(project_id, setup))
|
project_id=project_id,
|
||||||
|
reindex=reindex_project,
|
||||||
|
setup_response=_project_setup_response,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/version")
|
@app.get("/version")
|
||||||
|
|||||||
Reference in New Issue
Block a user