Skip to content

Commit 78fc88b

Browse files
Merge pull request #26 from Open-MBEE/develop
Develop
2 parents bbbcc48 + 661d984 commit 78fc88b

2 files changed

Lines changed: 363 additions & 0 deletions

File tree

src/flexo_syside_lib/committer.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
"""
2+
flexo_commit_helper.py
3+
4+
Utility for committing SysMLv2 textual models to a Flexo SysIDE server.
5+
6+
Typical usage:
7+
python flexo_commit_helper.py
8+
"""
9+
10+
from __future__ import annotations
11+
from typing import Optional, Dict, Tuple
12+
import os
13+
import pathlib
14+
from pprint import pprint
15+
16+
import syside_license
17+
from sysmlv2_client import SysMLV2Client
18+
from flexo_syside_lib.core import convert_sysml_string_textual_to_json
19+
from sysml_api.api_lib import (
20+
create_sysml_project,
21+
get_project_by_name,
22+
commit_to_project,
23+
)
24+
25+
26+
# === Constants ===
27+
DEFAULT_FLEXO_URL = "http://localhost:8080/"
28+
DEFAULT_PROJECT_NAME = "Flexo_SysIDE_TestProject"
29+
ENV_FILE = ".env"
30+
31+
32+
# === Environment Management ===
33+
def load_env_from_repo_root(filename: str = ENV_FILE) -> Optional[str]:
34+
"""
35+
Load key=value pairs from a `.env` file located in the current directory
36+
or any parent directory into `os.environ`.
37+
38+
Args:
39+
filename: The name of the environment file to search for.
40+
41+
Returns:
42+
The absolute path to the loaded `.env` file, or None if not found.
43+
"""
44+
cwd = pathlib.Path(__file__).resolve().parent
45+
for parent in [cwd] + list(cwd.parents):
46+
env_path = parent / filename
47+
if env_path.exists():
48+
with env_path.open("r") as f:
49+
for line in f:
50+
line = line.strip()
51+
if not line or line.startswith("#") or "=" not in line:
52+
continue
53+
key, value = line.split("=", 1)
54+
os.environ.setdefault(key.strip(), value.strip())
55+
56+
print(f"[ENV] Loaded environment variables from: {env_path}")
57+
return str(env_path)
58+
59+
print("[ENV] No .env file found in repo hierarchy.")
60+
return None
61+
62+
63+
def _default_base_and_token() -> Tuple[str, str]:
64+
"""
65+
Construct the Flexo base URL and Bearer token from environment variables.
66+
67+
Returns:
68+
(base_url, bearer_token) tuple.
69+
70+
Raises:
71+
EnvironmentError: if FLEXO_API_KEY is not set.
72+
"""
73+
api_key = os.getenv("FLEXO_API_KEY")
74+
if not api_key:
75+
raise EnvironmentError("Missing FLEXO_API_KEY environment variable.")
76+
77+
flexo_url = os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
78+
base_url = flexo_url.rstrip("/") + "/"
79+
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"
80+
81+
return base_url, bearer_token
82+
83+
84+
# === Core Commit Function ===
85+
def commit_sysml_to_flexo(
86+
sysml_output: str,
87+
project_name: str,
88+
api_key: Optional[str] = None,
89+
project_id: Optional[str] = None,
90+
flexo_url: Optional[str] = None,
91+
verbose: bool = True,
92+
) -> Dict[str, object]:
93+
"""
94+
Commit SysMLv2 textual content to a Flexo SysIDE server project.
95+
96+
Steps:
97+
1. Convert the SysML text to Flexo JSON format.
98+
2. Resolve or create the target project.
99+
3. Commit the change and return commit metadata.
100+
101+
Args:
102+
sysml_output: SysMLv2 textual representation.
103+
project_name: Target project name.
104+
api_key: Optional override for FLEXO_API_KEY.
105+
project_id: Optional override for an existing project ID.
106+
flexo_url: Optional override for FLEXO_URL.
107+
verbose: Print diagnostic information if True.
108+
109+
Returns:
110+
Dictionary containing base_url, project_id, commit_id, created_project,
111+
and the raw commit response.
112+
113+
Raises:
114+
EnvironmentError: If credentials are missing.
115+
RuntimeError: If commit or connection fails.
116+
"""
117+
api_key = api_key or os.getenv("FLEXO_API_KEY")
118+
if not api_key:
119+
raise EnvironmentError("Missing FLEXO_API_KEY environment variable or function argument.")
120+
121+
flexo_url = flexo_url or os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
122+
base_url = flexo_url.rstrip("/") + "/"
123+
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"
124+
125+
if verbose:
126+
print(f"[Flexo] Base URL: {base_url}")
127+
print(f"[Flexo] Target project: name='{project_name}', id={project_id}")
128+
print(f"[Flexo] Using token prefix: {api_key[:10]}...")
129+
130+
# --- Build client ---
131+
try:
132+
client = SysMLV2Client(base_url=base_url, bearer_token=bearer_token)
133+
if verbose:
134+
print("[Flexo] SysMLV2Client initialized successfully.")
135+
except Exception as e:
136+
raise RuntimeError(f"Failed to initialize SysMLV2Client: {e}") from e
137+
138+
# --- Convert SysML text to JSON ---
139+
try:
140+
change_payload_str, _ = convert_sysml_string_textual_to_json(sysml_output)
141+
if verbose:
142+
print("[Flexo] Converted SysML text to JSON payload.")
143+
except Exception as e:
144+
raise RuntimeError(f"SysML text conversion failed: {e}") from e
145+
146+
# --- Resolve or create project ---
147+
created_project = False
148+
proj_id = project_id
149+
150+
if not proj_id:
151+
project, found_id = get_project_by_name(client, project_name)
152+
if found_id:
153+
proj_id = found_id
154+
if verbose:
155+
print(f"[Flexo] Found existing project '{project_name}' -> id: {proj_id}")
156+
else:
157+
_, created_id, _ = create_sysml_project(client, project_name)
158+
proj_id = created_id
159+
created_project = True
160+
if verbose:
161+
print(f"[Flexo] Created new project '{project_name}' -> id: {proj_id}")
162+
else:
163+
if verbose:
164+
print(f"[Flexo] Using provided project ID: {proj_id}")
165+
166+
# --- Commit the change ---
167+
try:
168+
commit_response, commit_id = commit_to_project(client, proj_id, change_payload_str)
169+
if verbose:
170+
print(f"[Flexo] Commit response: {commit_response}")
171+
print(f"[Flexo] Commit successful, ID: {commit_id}")
172+
except Exception as e:
173+
raise RuntimeError(f"Commit operation failed: {e}") from e
174+
175+
if not commit_id:
176+
raise RuntimeError(f"Commit failed: {commit_response}")
177+
178+
return {
179+
"base_url": base_url,
180+
"project_id": proj_id,
181+
"commit_id": commit_id,
182+
"created_project": created_project,
183+
"commit_response": commit_response,
184+
}
185+
186+
187+
# === Simple Test Harness ===
188+
def test_commit_to_flexo() -> None:
189+
"""Verify that a SysMLv2 snippet can be committed to the Flexo server."""
190+
sysml_sample = """
191+
package TestPackage {
192+
part Satellite {
193+
attribute mass = 500.0;
194+
}
195+
}
196+
"""
197+
print("[TEST] Starting Flexo commit test...")
198+
result = commit_sysml_to_flexo(
199+
sysml_output=sysml_sample,
200+
project_name=DEFAULT_PROJECT_NAME,
201+
verbose=True,
202+
)
203+
print("\n[TEST RESULT]")
204+
pprint(result)
205+
206+
207+
# === Entrypoint ===
208+
if __name__ == "__main__":
209+
load_env_from_repo_root()
210+
211+
license_key = os.getenv("SYSIDE_LICENSE_KEY")
212+
if not license_key:
213+
raise EnvironmentError("Missing SYSIDE_LICENSE_KEY environment variable.")
214+
215+
syside_license.check(license_key)
216+
217+
test_commit_to_flexo()

src/flexo_syside_lib/retriever.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
flexo_model_retriever.py
3+
4+
Utility script to authenticate with a Flexo SysMLv2 server, locate a project,
5+
fetch its latest commit, and convert the model snapshot to textual SysML form.
6+
7+
How to test:
8+
python flexo_model_retriever.py
9+
"""
10+
11+
from __future__ import annotations
12+
from typing import Tuple, Optional
13+
import os
14+
import pathlib
15+
16+
import syside_license
17+
from sysmlv2_client import SysMLV2Client
18+
from flexo_syside_lib.core import convert_json_to_sysml_textual
19+
from sysml_api.api_lib import get_project_by_name, get_last_commit_from_project
20+
21+
22+
# === Constants ===
23+
DEFAULT_PROJECT_NAME = "Flexo_SysIDE_TestProject"
24+
DEFAULT_FLEXO_URL = "http://localhost:8080/"
25+
ENV_FILE = ".env"
26+
27+
28+
# === Environment Helpers ===
29+
def load_env_from_repo_root(filename: str = ENV_FILE) -> Optional[str]:
30+
"""
31+
Search for a `.env` file in the current directory or its parents,
32+
load key=value pairs into `os.environ`, and return the path to the file.
33+
34+
Args:
35+
filename: Name of the environment file to search for (default: ".env").
36+
37+
Returns:
38+
The path to the loaded .env file as a string, or None if not found.
39+
"""
40+
cwd = pathlib.Path(__file__).resolve().parent
41+
42+
for parent in [cwd] + list(cwd.parents):
43+
env_path = parent / filename
44+
if env_path.exists():
45+
with env_path.open("r") as f:
46+
for line in f:
47+
line = line.strip()
48+
if not line or line.startswith("#") or "=" not in line:
49+
continue
50+
key, value = line.split("=", 1)
51+
os.environ.setdefault(key.strip(), value.strip())
52+
53+
print(f"[ENV] Loaded environment variables from: {env_path}")
54+
return str(env_path)
55+
56+
print("[ENV] No .env file found in repo hierarchy.")
57+
return None
58+
59+
60+
def _default_base_and_token() -> Tuple[str, str]:
61+
"""
62+
Construct the Flexo base URL and Bearer token from environment variables.
63+
64+
Returns:
65+
(base_url, bearer_token) tuple.
66+
67+
Raises:
68+
EnvironmentError: if FLEXO_API_KEY is not set.
69+
"""
70+
api_key = os.getenv("FLEXO_API_KEY")
71+
if not api_key:
72+
raise EnvironmentError("Missing FLEXO_API_KEY environment variable.")
73+
74+
flexo_url = os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
75+
base_url = flexo_url.rstrip("/") + "/"
76+
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"
77+
78+
return base_url, bearer_token
79+
80+
81+
# === Main Model Retrieval ===
82+
def retrieve_latest_sysml_full_model(
83+
project_name: str = DEFAULT_PROJECT_NAME,
84+
verbose: bool = True,
85+
) -> str:
86+
"""
87+
Retrieve the latest full SysMLv2 model snapshot for a given project.
88+
89+
Steps:
90+
1. Resolve the project by name on the Flexo server.
91+
2. Identify its latest commit.
92+
3. Fetch and convert all model elements to textual SysML.
93+
94+
Args:
95+
project_name: Name of the project to query.
96+
verbose: If True, print diagnostic information.
97+
98+
Returns:
99+
SysML textual representation of the model.
100+
101+
Raises:
102+
EnvironmentError: if required environment variables are missing.
103+
RuntimeError: if the project cannot be found.
104+
"""
105+
base_url, bearer_token = _default_base_and_token()
106+
107+
if verbose:
108+
print(f"[Flexo] Base URL: {base_url}")
109+
print(f"[Flexo] Resolving project: '{project_name}'")
110+
111+
client = SysMLV2Client(base_url=base_url, bearer_token=bearer_token)
112+
113+
# --- Project lookup ---
114+
project_obj, project_id = get_project_by_name(client, project_name)
115+
if not project_id:
116+
raise RuntimeError(f"Project '{project_name}' not found on server {base_url}")
117+
118+
if verbose:
119+
print(f"[Flexo] Found project '{project_name}' with ID: {project_id}")
120+
121+
# --- Latest commit lookup ---
122+
latest_commit_id = get_last_commit_from_project(client, project_obj)
123+
if verbose and latest_commit_id:
124+
print(f"[Flexo] Latest commit ID: {latest_commit_id}")
125+
126+
# --- Model retrieval ---
127+
elements = client.list_elements(project_id, latest_commit_id)
128+
sysml_text, _ = convert_json_to_sysml_textual(elements)
129+
130+
return sysml_text
131+
132+
133+
# === Entrypoint ===
134+
if __name__ == "__main__":
135+
load_env_from_repo_root()
136+
137+
license_key = os.getenv("SYSIDE_LICENSE_KEY")
138+
if not license_key:
139+
raise EnvironmentError("Missing SYSIDE_LICENSE_KEY environment variable.")
140+
141+
syside_license.check(license_key)
142+
143+
sysml_textual = retrieve_latest_sysml_full_model()
144+
145+
print("\n=== Latest Commit (Full Model) ===\n")
146+
print(sysml_textual)

0 commit comments

Comments
 (0)