Skip to content

Commit e13a139

Browse files
committed
feat: add public_url support, full URL parsing, and overwrite for PrefectFlow registration
- Add public_url parameter to DeployParam and register_flow() for cases where PREFECT_API_URL differs from the URL clients should use - Parse full URL components (scheme, domain, port, path, query, fragment) into PrefectFlow entity fields - Force overwrite when storing Software/PrefectFlow entities
1 parent e8e115c commit e13a139

2 files changed

Lines changed: 40 additions & 3 deletions

File tree

‎examples/prefect/hello_world.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ def dummy_workflow(request: Request):
128128
# Direct run: dummy_workflow(Request(msg="Test"))
129129

130130
# Deploy and serve with OSW registration
131+
from os import environ
132+
131133
osw_instance = connect()
132134
deploy(
133135
DeployParam(
@@ -138,5 +140,6 @@ def dummy_workflow(request: Request):
138140
)
139141
],
140142
osw=osw_instance,
143+
public_url=environ.get("PREFECT_PUBLIC_URL"),
141144
)
142145
)

‎src/osw/utils/workflow.py‎

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ class DeployParam(BaseModel):
227227
namespace_uuid: Optional[UUID] = None
228228
"""Static UUIDv5 namespace for stable Software entity UUIDs.
229229
If None, a default namespace is used."""
230+
public_url: Optional[str] = None
231+
"""Public Prefect API URL stored in the PrefectFlow entity.
232+
Use this when the PREFECT_API_URL used by the worker differs from the
233+
URL clients (e.g. prefect.js) should use to reach the API.
234+
Example: PREFECT_API_URL='http://localhost:4200/api' but clients need
235+
'https://example.com/w/rest.php/apigateway'."""
230236

231237
class Config:
232238
arbitrary_types_allowed = True
@@ -240,6 +246,7 @@ async def register_flow(
240246
osw_instance: OSW,
241247
flow: Flow,
242248
namespace_uuid: Optional[UUID] = None,
249+
public_url: Optional[str] = None,
243250
) -> None:
244251
"""Register a Prefect flow as Software + PrefectFlow entities on OSW.
245252
@@ -256,6 +263,9 @@ async def register_flow(
256263
namespace_uuid
257264
UUIDv5 namespace for generating the Software entity UUID.
258265
If None, uses a default namespace.
266+
public_url
267+
Public Prefect API URL to store in the PrefectFlow entity.
268+
If None, PREFECT_API_URL env var is used.
259269
"""
260270
if namespace_uuid is None:
261271
namespace_uuid = _DEFAULT_NAMESPACE_UUID
@@ -285,15 +295,36 @@ async def register_flow(
285295
)
286296

287297
# create PrefectFlow entity
288-
prefect_api_url = environ.get("PREFECT_API_URL", "")
289-
prefect_domain = prefect_api_url.split("//")[-1].split("/")[0]
298+
# use public_url if provided, otherwise fall back to PREFECT_API_URL
299+
from urllib.parse import urlparse
300+
301+
entity_url = public_url or environ.get("PREFECT_API_URL", "")
302+
parsed = urlparse(entity_url)
303+
prefect_domain = parsed.hostname or ""
304+
prefect_schema = parsed.scheme or "https"
305+
prefect_port = [parsed.port] if parsed.port else None
306+
prefect_path = parsed.path or None
307+
# reconstruct full URL preserving query params and fragment
308+
prefect_url = (
309+
f"{prefect_schema}://{prefect_domain}"
310+
+ (f":{parsed.port}" if parsed.port else "")
311+
+ (prefect_path or "")
312+
)
313+
if parsed.query:
314+
prefect_url += f"?{parsed.query}"
315+
if parsed.fragment:
316+
prefect_url += f"#{parsed.fragment}"
290317
this_flow = model.PrefectFlow(
291318
uuid=flow_uuid,
292319
label=[model.Label(text=flow.name + " Prefect Flow")],
293320
description=[model.Description(text=flow.description or "")],
294321
flow_id=str(flow_uuid),
295322
hosted_software=[get_full_title(this_tool)],
296323
domain=prefect_domain,
324+
schema_=prefect_schema,
325+
network_port=prefect_port,
326+
url_path=prefect_path,
327+
url=prefect_url,
297328
)
298329

299330
# delete stale PrefectFlow entities that reference the same Software
@@ -318,7 +349,9 @@ async def register_flow(
318349
comment="Replaced by updated PrefectFlow deployment",
319350
)
320351

321-
osw_instance.store_entity(OSW.StoreEntityParam(entities=[this_tool, this_flow]))
352+
osw_instance.store_entity(
353+
OSW.StoreEntityParam(entities=[this_tool, this_flow], overwrite=True)
354+
)
322355

323356
# build parameters template from flow function signature
324357
# includes all fields so users can see and customize them
@@ -414,6 +447,7 @@ async def _deploy(param: DeployParam):
414447
osw_instance=param.osw,
415448
flow=flow,
416449
namespace_uuid=param.namespace_uuid,
450+
public_url=param.public_url,
417451
)
418452

419453
if version("prefect") in SpecifierSet(">=3.0"):

0 commit comments

Comments
 (0)