Skip to content

Commit ea8a24d

Browse files
Add connection check before starting rendering
1 parent 7b5a5f6 commit ea8a24d

5 files changed

Lines changed: 55 additions & 5 deletions

File tree

codeplain_REST_api.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ def _extend_payload_with_run_state(self, payload: dict, run_state: RunState):
3333
run_state.increment_call_count()
3434
payload["render_state"] = run_state.to_dict()
3535

36-
def post_request(self, endpoint_url, headers, payload, run_state: Optional[RunState]): # noqa: C901
36+
def post_request(
37+
self, endpoint_url, headers, payload, run_state: Optional[RunState], num_retries: int = MAX_RETRIES
38+
): # noqa: C901
3739
if run_state is not None:
3840
self._extend_payload_with_run_state(payload, run_state)
3941

4042
retry_delay = RETRY_DELAY
4143
response_json = None
42-
for attempt in range(MAX_RETRIES + 1):
44+
for attempt in range(num_retries + 1):
4345
try:
4446
response = requests.post(endpoint_url, headers=headers, json=payload)
4547

@@ -81,16 +83,25 @@ def post_request(self, endpoint_url, headers, payload, run_state: Optional[RunSt
8183
if response_json["error_code"] not in RETRY_ERROR_CODES:
8284
raise e
8385

84-
if attempt < MAX_RETRIES:
85-
self.console.info(f"Error on attempt {attempt + 1}/{MAX_RETRIES + 1}: {e}")
86+
if attempt < num_retries:
87+
self.console.info(f"Error on attempt {attempt + 1}/{num_retries + 1}: {e}")
8688
self.console.info(f"Retrying in {retry_delay} seconds...")
8789
time.sleep(retry_delay)
8890
# Exponential backoff
8991
retry_delay *= 2
9092
else:
91-
self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Last error: {e}")
93+
self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {e}")
9294
raise e
9395

96+
def connection_check(self, client_version):
97+
endpoint_url = f"{self.api_url}/connection_check"
98+
headers = {"Content-Type": "application/json"}
99+
payload = {
100+
"api_key": self.api_key,
101+
"client_version": client_version,
102+
}
103+
return self.post_request(endpoint_url, headers, payload, None, num_retries=0)
104+
94105
def render_functional_requirement(
95106
self,
96107
frid: str,

config/system_config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
client_version: "0.17.0"
2+
13
system_requirements:
24
timeout:
35
command: timeout

plain2code.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
ConflictingRequirements,
2222
CreditBalanceTooLow,
2323
InternalServerError,
24+
InvalidAPIKey,
2425
InvalidFridArgument,
2526
LLMInternalError,
2627
MissingAPIKey,
2728
MissingPreviousFunctionalitiesError,
2829
MissingResource,
30+
OutdatedClientVersion,
2931
PlainSyntaxError,
3032
UnexpectedState,
3133
)
@@ -163,6 +165,24 @@ def setup_logging(
163165
root_logger.info(f"Render ID: {render_id}") # Ensure render ID is logged in to codeplain.log file
164166

165167

168+
def _check_connection(codeplainAPI):
169+
"""Check API connectivity and validate API key and client version."""
170+
response = codeplainAPI.connection_check(system_config.client_version)
171+
172+
if not response.get("api_key_valid", False):
173+
raise InvalidAPIKey(
174+
"Please provide a valid API key using the CODEPLAIN_API_KEY environment variable "
175+
"or the --api-key argument."
176+
)
177+
178+
if not response.get("client_version_valid", False):
179+
min_version = response.get("min_client_version", "unknown")
180+
raise OutdatedClientVersion(
181+
f"Your client version ({system_config.client_version}) is outdated. Minimum required version is {min_version}. "
182+
"Please update using: uv tool upgrade codeplain"
183+
)
184+
185+
166186
def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # noqa: C901
167187
# Check system requirements before proceeding
168188
system_config.verify_requirements()
@@ -187,6 +207,8 @@ def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # no
187207
assert args.api is not None and args.api != "", "API URL is required"
188208
codeplainAPI.api_url = args.api
189209

210+
_check_connection(codeplainAPI)
211+
190212
module_renderer = ModuleRenderer(
191213
codeplainAPI,
192214
args.filename,
@@ -266,6 +288,10 @@ def main(): # noqa: C901
266288
console.debug(f"Render ID: {run_state.render_id}")
267289
except MissingAPIKey as e:
268290
console.error(f"Missing API key: {str(e)}\n")
291+
except InvalidAPIKey as e:
292+
console.error(f"Invalid API key: {str(e)}\n")
293+
except OutdatedClientVersion as e:
294+
console.error(f"Outdated client version: {str(e)}\n")
269295
except (InternalServerError, UnexpectedState):
270296
exc_info = sys.exc_info()
271297
console.error(

plain2code_exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ class MissingAPIKey(Exception):
3333
pass
3434

3535

36+
class InvalidAPIKey(Exception):
37+
pass
38+
39+
40+
class OutdatedClientVersion(Exception):
41+
pass
42+
43+
3644
class InvalidFridArgument(Exception):
3745
pass
3846

system_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ class SystemConfig:
1212

1313
def __init__(self):
1414
self.config = self._load_config()
15+
if "client_version" not in self.config:
16+
raise KeyError("Missing 'client_version' in system_config.yaml")
1517
if "system_requirements" not in self.config:
1618
raise KeyError("Missing 'system_requirements' section in system_config.yaml")
1719
if "error_messages" not in self.config:
1820
raise KeyError("Missing 'error_messages' section in system_config.yaml")
1921

22+
self.client_version = self.config["client_version"]
2023
self.requirements = self.config["system_requirements"]
2124
self.error_messages = self.config["error_messages"]
2225

0 commit comments

Comments
 (0)