Skip to content

Commit 3dc87af

Browse files
Alfonso Sastrecursoragent
andcommitted
fix: handle OCPI version negotiation in push_object
push_object now accepts both a versions URL and a version details URL as endpoints_url. When a versions list is returned (data is a list), it automatically picks the best mutual version and fetches the details URL to discover endpoints. This follows the proper OCPI spec flow: 1. GET /versions → list of supported versions with details URLs 2. Pick best mutual version 3. GET /{version}/details → list of endpoints 4. Push to the correct module endpoint Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 22e5032 commit 3dc87af

1 file changed

Lines changed: 50 additions & 1 deletion

File tree

ocpi/core/push.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@
1616
from ocpi.modules.versions.v_2_2_1.enums import InterfaceRole
1717

1818

19+
_VERSION_PREFERENCE = ["2.3.0", "2.2.1", "2.1.1"]
20+
21+
22+
def _pick_version_details_url(
23+
versions_list: list[dict], requested: VersionNumber
24+
) -> str | None:
25+
"""Pick the best version details URL from an OCPI /versions response.
26+
27+
Tries the requested version first, then falls back to the highest
28+
mutually supported version.
29+
"""
30+
by_version = {v["version"]: v["url"] for v in versions_list if "version" in v}
31+
32+
if requested.value in by_version:
33+
return by_version[requested.value]
34+
35+
for v in _VERSION_PREFERENCE:
36+
if v in by_version:
37+
return by_version[v]
38+
39+
return None
40+
41+
1942
def client_url(module_id: ModuleID, object_id: str, base_url: str) -> str:
2043
if module_id == ModuleID.cdrs:
2144
return base_url
@@ -106,7 +129,33 @@ async def push_object(
106129
headers={"authorization": client_auth_token},
107130
)
108131
logger.info(f"Response status_code - `{response.status_code}`")
109-
endpoints = response.json()["data"]["endpoints"]
132+
response_data = response.json()["data"]
133+
134+
# If response is a versions list, negotiate version and
135+
# fetch the details URL for the best mutual version.
136+
if isinstance(response_data, list):
137+
details_url = _pick_version_details_url(
138+
response_data, version
139+
)
140+
if not details_url:
141+
raise ValueError(
142+
f"No mutual OCPI version found. "
143+
f"Requested {version.value}, receiver supports: "
144+
f"{[v.get('version') for v in response_data]}"
145+
)
146+
logger.info(
147+
f"Resolved version details URL: {details_url}"
148+
)
149+
response = await client.get(
150+
details_url,
151+
headers={"authorization": client_auth_token},
152+
)
153+
logger.info(
154+
f"Version details response: {response.status_code}"
155+
)
156+
response_data = response.json()["data"]
157+
158+
endpoints = response_data["endpoints"]
110159
logger.debug(f"Endpoints response data - `{endpoints}`")
111160

112161
# get object data

0 commit comments

Comments
 (0)