Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions src/flexo_syside_lib/committer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
"""
flexo_commit_helper.py

Utility for committing SysMLv2 textual models to a Flexo SysIDE server.

Typical usage:
python flexo_commit_helper.py
"""

from __future__ import annotations
from typing import Optional, Dict, Tuple
import os
import pathlib
from pprint import pprint

import syside_license
from sysmlv2_client import SysMLV2Client
from flexo_syside_lib.core import convert_sysml_string_textual_to_json
from sysml_api.api_lib import (
create_sysml_project,
get_project_by_name,
commit_to_project,
)


# === Constants ===
DEFAULT_FLEXO_URL = "http://localhost:8080/"
DEFAULT_PROJECT_NAME = "Flexo_SysIDE_TestProject"
ENV_FILE = ".env"


# === Environment Management ===
def load_env_from_repo_root(filename: str = ENV_FILE) -> Optional[str]:
"""
Load key=value pairs from a `.env` file located in the current directory
or any parent directory into `os.environ`.

Args:
filename: The name of the environment file to search for.

Returns:
The absolute path to the loaded `.env` file, or None if not found.
"""
cwd = pathlib.Path(__file__).resolve().parent
for parent in [cwd] + list(cwd.parents):
env_path = parent / filename
if env_path.exists():
with env_path.open("r") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
os.environ.setdefault(key.strip(), value.strip())

print(f"[ENV] Loaded environment variables from: {env_path}")
return str(env_path)

print("[ENV] No .env file found in repo hierarchy.")
return None


def _default_base_and_token() -> Tuple[str, str]:
"""
Construct the Flexo base URL and Bearer token from environment variables.

Returns:
(base_url, bearer_token) tuple.

Raises:
EnvironmentError: if FLEXO_API_KEY is not set.
"""
api_key = os.getenv("FLEXO_API_KEY")
if not api_key:
raise EnvironmentError("Missing FLEXO_API_KEY environment variable.")

flexo_url = os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
base_url = flexo_url.rstrip("/") + "/"
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"

return base_url, bearer_token


# === Core Commit Function ===
def commit_sysml_to_flexo(
sysml_output: str,
project_name: str,
api_key: Optional[str] = None,
project_id: Optional[str] = None,
flexo_url: Optional[str] = None,
verbose: bool = True,
) -> Dict[str, object]:
"""
Commit SysMLv2 textual content to a Flexo SysIDE server project.

Steps:
1. Convert the SysML text to Flexo JSON format.
2. Resolve or create the target project.
3. Commit the change and return commit metadata.

Args:
sysml_output: SysMLv2 textual representation.
project_name: Target project name.
api_key: Optional override for FLEXO_API_KEY.
project_id: Optional override for an existing project ID.
flexo_url: Optional override for FLEXO_URL.
verbose: Print diagnostic information if True.

Returns:
Dictionary containing base_url, project_id, commit_id, created_project,
and the raw commit response.

Raises:
EnvironmentError: If credentials are missing.
RuntimeError: If commit or connection fails.
"""
api_key = api_key or os.getenv("FLEXO_API_KEY")
if not api_key:
raise EnvironmentError("Missing FLEXO_API_KEY environment variable or function argument.")

flexo_url = flexo_url or os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
base_url = flexo_url.rstrip("/") + "/"
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"

if verbose:
print(f"[Flexo] Base URL: {base_url}")
print(f"[Flexo] Target project: name='{project_name}', id={project_id}")
print(f"[Flexo] Using token prefix: {api_key[:10]}...")

# --- Build client ---
try:
client = SysMLV2Client(base_url=base_url, bearer_token=bearer_token)
if verbose:
print("[Flexo] SysMLV2Client initialized successfully.")
except Exception as e:
raise RuntimeError(f"Failed to initialize SysMLV2Client: {e}") from e

# --- Convert SysML text to JSON ---
try:
change_payload_str, _ = convert_sysml_string_textual_to_json(sysml_output)
if verbose:
print("[Flexo] Converted SysML text to JSON payload.")
except Exception as e:
raise RuntimeError(f"SysML text conversion failed: {e}") from e

# --- Resolve or create project ---
created_project = False
proj_id = project_id

if not proj_id:
project, found_id = get_project_by_name(client, project_name)
if found_id:
proj_id = found_id
if verbose:
print(f"[Flexo] Found existing project '{project_name}' -> id: {proj_id}")
else:
_, created_id, _ = create_sysml_project(client, project_name)
proj_id = created_id
created_project = True
if verbose:
print(f"[Flexo] Created new project '{project_name}' -> id: {proj_id}")
else:
if verbose:
print(f"[Flexo] Using provided project ID: {proj_id}")

# --- Commit the change ---
try:
commit_response, commit_id = commit_to_project(client, proj_id, change_payload_str)
if verbose:
print(f"[Flexo] Commit response: {commit_response}")
print(f"[Flexo] Commit successful, ID: {commit_id}")
except Exception as e:
raise RuntimeError(f"Commit operation failed: {e}") from e

if not commit_id:
raise RuntimeError(f"Commit failed: {commit_response}")

return {
"base_url": base_url,
"project_id": proj_id,
"commit_id": commit_id,
"created_project": created_project,
"commit_response": commit_response,
}


# === Simple Test Harness ===
def test_commit_to_flexo() -> None:
"""Verify that a SysMLv2 snippet can be committed to the Flexo server."""
sysml_sample = """
package TestPackage {
part Satellite {
attribute mass = 500.0;
}
}
"""
print("[TEST] Starting Flexo commit test...")
result = commit_sysml_to_flexo(
sysml_output=sysml_sample,
project_name=DEFAULT_PROJECT_NAME,
verbose=True,
)
print("\n[TEST RESULT]")
pprint(result)


# === Entrypoint ===
if __name__ == "__main__":
load_env_from_repo_root()

license_key = os.getenv("SYSIDE_LICENSE_KEY")
if not license_key:
raise EnvironmentError("Missing SYSIDE_LICENSE_KEY environment variable.")

syside_license.check(license_key)

test_commit_to_flexo()
146 changes: 146 additions & 0 deletions src/flexo_syside_lib/retriever.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""
flexo_model_retriever.py

Utility script to authenticate with a Flexo SysMLv2 server, locate a project,
fetch its latest commit, and convert the model snapshot to textual SysML form.

How to test:
python flexo_model_retriever.py
"""

from __future__ import annotations
from typing import Tuple, Optional
import os
import pathlib

import syside_license
from sysmlv2_client import SysMLV2Client
from flexo_syside_lib.core import convert_json_to_sysml_textual
from sysml_api.api_lib import get_project_by_name, get_last_commit_from_project


# === Constants ===
DEFAULT_PROJECT_NAME = "Flexo_SysIDE_TestProject"
DEFAULT_FLEXO_URL = "http://localhost:8080/"
ENV_FILE = ".env"


# === Environment Helpers ===
def load_env_from_repo_root(filename: str = ENV_FILE) -> Optional[str]:
"""
Search for a `.env` file in the current directory or its parents,
load key=value pairs into `os.environ`, and return the path to the file.

Args:
filename: Name of the environment file to search for (default: ".env").

Returns:
The path to the loaded .env file as a string, or None if not found.
"""
cwd = pathlib.Path(__file__).resolve().parent

for parent in [cwd] + list(cwd.parents):
env_path = parent / filename
if env_path.exists():
with env_path.open("r") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
os.environ.setdefault(key.strip(), value.strip())

print(f"[ENV] Loaded environment variables from: {env_path}")
return str(env_path)

print("[ENV] No .env file found in repo hierarchy.")
return None


def _default_base_and_token() -> Tuple[str, str]:
"""
Construct the Flexo base URL and Bearer token from environment variables.

Returns:
(base_url, bearer_token) tuple.

Raises:
EnvironmentError: if FLEXO_API_KEY is not set.
"""
api_key = os.getenv("FLEXO_API_KEY")
if not api_key:
raise EnvironmentError("Missing FLEXO_API_KEY environment variable.")

flexo_url = os.getenv("FLEXO_URL", DEFAULT_FLEXO_URL)
base_url = flexo_url.rstrip("/") + "/"
bearer_token = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"

return base_url, bearer_token


# === Main Model Retrieval ===
def retrieve_latest_sysml_full_model(
project_name: str = DEFAULT_PROJECT_NAME,
verbose: bool = True,
) -> str:
"""
Retrieve the latest full SysMLv2 model snapshot for a given project.

Steps:
1. Resolve the project by name on the Flexo server.
2. Identify its latest commit.
3. Fetch and convert all model elements to textual SysML.

Args:
project_name: Name of the project to query.
verbose: If True, print diagnostic information.

Returns:
SysML textual representation of the model.

Raises:
EnvironmentError: if required environment variables are missing.
RuntimeError: if the project cannot be found.
"""
base_url, bearer_token = _default_base_and_token()

if verbose:
print(f"[Flexo] Base URL: {base_url}")
print(f"[Flexo] Resolving project: '{project_name}'")

client = SysMLV2Client(base_url=base_url, bearer_token=bearer_token)

# --- Project lookup ---
project_obj, project_id = get_project_by_name(client, project_name)
if not project_id:
raise RuntimeError(f"Project '{project_name}' not found on server {base_url}")

if verbose:
print(f"[Flexo] Found project '{project_name}' with ID: {project_id}")

# --- Latest commit lookup ---
latest_commit_id = get_last_commit_from_project(client, project_obj)
if verbose and latest_commit_id:
print(f"[Flexo] Latest commit ID: {latest_commit_id}")

# --- Model retrieval ---
elements = client.list_elements(project_id, latest_commit_id)
sysml_text, _ = convert_json_to_sysml_textual(elements)

return sysml_text


# === Entrypoint ===
if __name__ == "__main__":
load_env_from_repo_root()

license_key = os.getenv("SYSIDE_LICENSE_KEY")
if not license_key:
raise EnvironmentError("Missing SYSIDE_LICENSE_KEY environment variable.")

syside_license.check(license_key)

sysml_textual = retrieve_latest_sysml_full_model()

print("\n=== Latest Commit (Full Model) ===\n")
print(sysml_textual)