Skip to content

Commit d071fa5

Browse files
committed
Update nexus operation and activity link formats to match new expected server shape
1 parent 6217763 commit d071fa5

2 files changed

Lines changed: 15 additions & 70 deletions

File tree

temporalio/nexus/_link_conversion.py

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
logger = logging.getLogger(__name__)
2121

2222
_NEXUS_OPERATION_LINK_URL_PATH_REGEX = re.compile(
23-
r"^/namespaces/(?P<namespace>[^/]+)/nexus-operations/(?P<operation_id>[^/]+)$"
23+
r"^/namespaces/(?P<namespace>[^/]+)/nexus-operations/(?P<operation_id>[^/]+)/(?P<run_id>[^/]+)/details$"
2424
)
2525

2626
_ACTIVITY_LINK_URL_PATH_REGEX = re.compile(
27-
r"^/namespaces/(?P<namespace>[^/]+)/activities/(?P<activity_id>[^/]+)$"
27+
r"^/namespaces/(?P<namespace>[^/]+)/activities/(?P<activity_id>[^/]+)/(?P<run_id>[^/]+)/details$"
2828
)
2929

3030
_WORFKLOW_LINK_URL_PATH_REGEX = re.compile(
@@ -42,7 +42,6 @@ class _LinkType(str, Enum):
4242
LINK_EVENT_TYPE_PARAM_NAME = "eventType"
4343
LINK_REQUEST_ID_PARAM_NAME = "requestID"
4444
LINK_REFERENCE_TYPE_PARAM_NAME = "referenceType"
45-
LINK_RUN_ID_PARAM_NAME = "runID"
4645

4746
EVENT_REFERENCE_TYPE = "EventReference"
4847
REQUEST_ID_REFERENCE_TYPE = "RequestIdReference"
@@ -160,18 +159,11 @@ def nexus_operation_to_nexus_link(
160159
scheme = "temporal"
161160
namespace = urllib.parse.quote(op_link.namespace, safe="")
162161
operation_id = urllib.parse.quote(op_link.operation_id, safe="")
163-
path = f"/namespaces/{namespace}/nexus-operations/{operation_id}"
164-
165-
query_params = ""
166-
if op_link.run_id:
167-
query_params = urllib.parse.urlencode(
168-
{
169-
LINK_RUN_ID_PARAM_NAME: op_link.run_id,
170-
},
171-
)
162+
run_id = urllib.parse.quote(op_link.run_id, safe="")
163+
path = f"/namespaces/{namespace}/nexus-operations/{operation_id}/{run_id}/details"
172164

173165
# urllib will omit '//' from the url if netloc is empty so we add the scheme manually
174-
url = f"{scheme}://{urllib.parse.urlunparse(('', '', path, '', query_params, ''))}"
166+
url = f"{scheme}://{urllib.parse.urlunparse(('', '', path, '', '', ''))}"
175167

176168
return nexusrpc.Link(url=url, type=_LinkType.NEXUS_OPERATION.value)
177169

@@ -183,14 +175,10 @@ def activity_link_to_nexus_link(
183175
scheme = "temporal"
184176
namespace = urllib.parse.quote(activity.namespace, safe="")
185177
activity_id = urllib.parse.quote(activity.activity_id, safe="")
186-
path = f"/namespaces/{namespace}/activities/{activity_id}"
178+
run_id = urllib.parse.quote(activity.run_id, safe="")
179+
path = f"/namespaces/{namespace}/activities/{activity_id}/{run_id}/details"
187180

188-
if activity.run_id:
189-
query_params = urllib.parse.urlencode({LINK_RUN_ID_PARAM_NAME: activity.run_id})
190-
else:
191-
query_params = ""
192-
193-
url = f"{scheme}://{urllib.parse.urlunparse(('', '', path, '', query_params, ''))}"
181+
url = f"{scheme}://{urllib.parse.urlunparse(('', '', path, '', '', ''))}"
194182

195183
return nexusrpc.Link(url=url, type=_LinkType.ACTIVITY.value)
196184

@@ -258,24 +246,11 @@ def nexus_link_to_nexus_operation_link(
258246
)
259247
return None
260248

261-
query_params = urllib.parse.parse_qs(url.query)
262-
263-
match query_params.get(LINK_RUN_ID_PARAM_NAME):
264-
case [run_id_param]:
265-
run_id = run_id_param
266-
case [] | None:
267-
run_id = ""
268-
case _:
269-
logger.warning(
270-
f"Invalid Nexus link: {nexus_link}. Expected {LINK_RUN_ID_PARAM_NAME} to have at most 1 value"
271-
)
272-
return None
273-
274249
groups = match.groupdict()
275250
nexus_op_link = temporalio.api.common.v1.Link.NexusOperation(
276251
namespace=urllib.parse.unquote(groups["namespace"]),
277252
operation_id=urllib.parse.unquote(groups["operation_id"]),
278-
run_id=run_id,
253+
run_id=urllib.parse.unquote(groups["run_id"]),
279254
)
280255
return temporalio.api.common.v1.Link(nexus_operation=nexus_op_link)
281256

@@ -292,22 +267,11 @@ def nexus_link_to_activity_link(
292267
)
293268
return None
294269

295-
query_params = urllib.parse.parse_qs(url.query, keep_blank_values=True)
296-
297-
match query_params.get(LINK_RUN_ID_PARAM_NAME):
298-
case [run_id_param]:
299-
run_id = run_id_param
300-
case _:
301-
logger.warning(
302-
f"Invalid Nexus link: {nexus_link}. Expected {LINK_RUN_ID_PARAM_NAME} to have exactly 1 value"
303-
)
304-
return None
305-
306270
groups = match.groupdict()
307271
activity_link = temporalio.api.common.v1.Link.Activity(
308272
namespace=urllib.parse.unquote(groups["namespace"]),
309273
activity_id=urllib.parse.unquote(groups["activity_id"]),
310-
run_id=run_id,
274+
run_id=urllib.parse.unquote(groups["run_id"]),
311275
)
312276
return temporalio.api.common.v1.Link(activity=activity_link)
313277

tests/nexus/test_link_conversion.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -222,31 +222,20 @@ def test_link_conversion_workflow_event_to_link_and_back(
222222
),
223223
nexusrpc.Link(
224224
type=temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name,
225-
url="temporal:///namespaces/ns/nexus-operations/op-id?runID=run-id",
226-
),
227-
),
228-
(
229-
temporalio.api.common.v1.Link(
230-
nexus_operation=temporalio.api.common.v1.Link.NexusOperation(
231-
namespace="ns",
232-
operation_id="op-id",
233-
)
234-
),
235-
nexusrpc.Link(
236-
type=temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name,
237-
url="temporal:///namespaces/ns/nexus-operations/op-id",
225+
url="temporal:///namespaces/ns/nexus-operations/op-id/run-id/details",
238226
),
239227
),
240228
(
241229
temporalio.api.common.v1.Link(
242230
nexus_operation=temporalio.api.common.v1.Link.NexusOperation(
243231
namespace="ns",
244232
operation_id="op/id",
233+
run_id="run/id",
245234
)
246235
),
247236
nexusrpc.Link(
248237
type=temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name,
249-
url="temporal:///namespaces/ns/nexus-operations/op%2Fid",
238+
url="temporal:///namespaces/ns/nexus-operations/op%2Fid/run%2Fid/details",
250239
),
251240
),
252241
],
@@ -277,21 +266,13 @@ def test_link_conversion_nexus_operation_to_link_and_back(
277266
)
278267

279268

280-
def test_nexus_operation_link_with_duplicate_run_id_is_ignored():
281-
link = nexusrpc.Link(
282-
type=temporalio.api.common.v1.Link.NexusOperation.DESCRIPTOR.full_name,
283-
url="temporal:///namespaces/ns/nexus-operations/op-id?runID=one&runID=two",
284-
)
285-
assert temporalio.nexus._link_conversion.nexus_link_to_temporal_link(link) is None
286-
287-
288269
@pytest.mark.parametrize(
289270
["link", "expected_link"],
290271
[
291272
(
292273
nexusrpc.Link(
293274
type=temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name,
294-
url="temporal:///namespaces/ns/activities/act-id?runID=run-id",
275+
url="temporal:///namespaces/ns/activities/act-id/run-id/details",
295276
),
296277
temporalio.api.common.v1.Link(
297278
activity=temporalio.api.common.v1.Link.Activity(
@@ -304,7 +285,7 @@ def test_nexus_operation_link_with_duplicate_run_id_is_ignored():
304285
(
305286
nexusrpc.Link(
306287
type=temporalio.api.common.v1.Link.Activity.DESCRIPTOR.full_name,
307-
url="temporal:///namespaces/ns%2F/activities/act-id%2F?runID=run-id%3E",
288+
url="temporal:///namespaces/ns%2F/activities/act-id%2F/run-id%3E/details",
308289
),
309290
temporalio.api.common.v1.Link(
310291
activity=temporalio.api.common.v1.Link.Activity(

0 commit comments

Comments
 (0)