Skip to content

Commit 4984770

Browse files
Oleksandr Muzyka (EPAM)Oleksandr Muzyka (EPAM)
authored andcommitted
refactor: reorganize test resources and improve SAMM CLI command handling
1 parent 089cabb commit 4984770

File tree

83 files changed

+585
-398
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+585
-398
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ dmypy.json
7070
# SAMM
7171
core/esmf-aspect-meta-model-python/esmf_aspect_meta_model_python/samm_aspect_meta_model/samm/
7272
/core/esmf-aspect-meta-model-python/samm-cli/
73-
/core/esmf-aspect-meta-model-python/tests/integration/java_models/resources/
73+
/core/esmf-aspect-meta-model-python/tests/integration/pymodels/java_models/resources/

core/esmf-aspect-meta-model-python/esmf_aspect_meta_model_python/samm_cli_functions.py

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
99
#
1010
# SPDX-License-Identifier: MPL-2.0
11-
import platform
12-
import shlex
1311
import subprocess
1412

1513
from os.path import exists, join
@@ -26,13 +24,42 @@ class SammCli:
2624
SAMM CLI version.
2725
"""
2826

27+
# Command names
28+
CMD_VALIDATE = "validate"
29+
CMD_TO_OPENAPI = "to openapi"
30+
CMD_TO_SCHEMA = "to schema"
31+
CMD_TO_JSON = "to json"
32+
CMD_TO_HTML = "to html"
33+
CMD_TO_PNG = "to png"
34+
CMD_TO_SVG = "to svg"
35+
CMD_PRETTYPRINT = "prettyprint"
36+
CMD_TO_JAVA = "to java"
37+
CMD_TO_ASYNCAPI = "to asyncapi"
38+
CMD_TO_JSONLD = "to jsonld"
39+
CMD_TO_SQL = "to sql"
40+
CMD_TO_AAS = "to aas"
41+
CMD_EDIT_MOVE = "edit move"
42+
CMD_EDIT_NEWVERSION = "edit newversion"
43+
CMD_USAGE = "usage"
44+
CMD_AAS_TO_ASPECT = "to aspect"
45+
CMD_AAS_LIST = "list"
46+
CMD_PACKAGE_IMPORT = "import"
47+
CMD_PACKAGE_EXPORT = "export"
48+
49+
# Command types
50+
TYPE_ASPECT = "aspect"
51+
TYPE_AAS = "aas"
52+
TYPE_PACKAGE = "package"
53+
54+
ALLOWED_COMMAND_TYPES = {TYPE_ASPECT, TYPE_AAS, TYPE_PACKAGE}
55+
2956
def __init__(self):
3057
self._samm = self._get_client_path()
3158
self._validate_client()
3259

3360
@staticmethod
3461
def _get_client_path():
35-
"""Get path to the SAMM CLI executable file.."""
62+
"""Get path to the SAMM CLI executable file."""
3663
base_path = Path(__file__).resolve()
3764
cli_path = join(base_path.parents[1], "samm-cli", "samm.exe")
3865

@@ -42,9 +69,28 @@ def _get_client_path():
4269
def _format_argument(key: str, value: Any) -> str:
4370
"""Helper method to format command line arguments."""
4471
if len(key) == 1:
45-
return f"-{key}={value}"
46-
key_formatted = key.replace("_", "-")
47-
return f"--{key_formatted}={value}"
72+
formatted = f"-{key}={value}"
73+
else:
74+
formatted = f"--{key.replace('_', '-')}={value}"
75+
return formatted
76+
77+
def _process_kwargs(self, kwargs: dict) -> list[str]:
78+
"""Process keyword arguments and return formatted command line arguments.
79+
80+
Args:
81+
kwargs: Dictionary of keyword arguments where values can be single items or lists/tuples
82+
83+
Returns:
84+
List of formatted command line arguments
85+
"""
86+
args = []
87+
for key, value in kwargs.items():
88+
if isinstance(value, (list, tuple)):
89+
for item in value:
90+
args.append(self._format_argument(key, item))
91+
else:
92+
args.append(self._format_argument(key, value))
93+
return args
4894

4995
def _validate_client(self):
5096
"""Validate SAMM CLI.
@@ -54,25 +100,22 @@ def _validate_client(self):
54100
if not exists(self._samm):
55101
download_samm_cli()
56102

57-
def _call_function(self, function_name, path_to_model, *args, command_type="aspect", **kwargs):
103+
def _call_function(self, function_name, path_to_model, *args, command_type=None, **kwargs):
58104
"""Run a SAMM CLI function as a subprocess."""
105+
if command_type is None:
106+
command_type = self.TYPE_ASPECT
107+
elif command_type not in self.ALLOWED_COMMAND_TYPES:
108+
raise ValueError(f"Invalid command type: '{command_type}'. ")
109+
59110
call_args = [self._samm, command_type, path_to_model] + function_name.split()
60111

61112
if args:
62113
call_args.extend([f"-{param}" for param in args])
63114

64115
if kwargs:
65-
for key, value in kwargs.items():
66-
if isinstance(value, (list, tuple)):
67-
for item in value:
68-
call_args.append(self._format_argument(key, item))
69-
else:
70-
call_args.append(self._format_argument(key, value))
71-
72-
if platform.system() == "Darwin": # macOS
73-
call_args = shlex.join(call_args)
116+
call_args.extend(self._process_kwargs(kwargs))
74117

75-
subprocess.run(call_args, shell=True, check=True)
118+
subprocess.run(call_args)
76119

77120
def validate(self, path_to_model, *args, **kwargs):
78121
"""Validate Aspect Model.
@@ -81,7 +124,7 @@ def validate(self, path_to_model, *args, **kwargs):
81124
possible arguments:
82125
custom-resolver: use an external resolver for the resolution of the model elements
83126
"""
84-
self._call_function("validate", path_to_model, *args, **kwargs)
127+
self._call_function(self.CMD_VALIDATE, path_to_model, *args, **kwargs)
85128

86129
def prettyprint(self, path_to_model, *args, **kwargs):
87130
"""Pretty-print Aspect Model.
@@ -94,7 +137,7 @@ def prettyprint(self, path_to_model, *args, **kwargs):
94137
- overwrite, w: overwrite the input file (use as flag without value)
95138
- custom-resolver: use an external resolver for the resolution of the model elements
96139
"""
97-
self._call_function("prettyprint", path_to_model, *args, **kwargs)
140+
self._call_function(self.CMD_PRETTYPRINT, path_to_model, *args, **kwargs)
98141

99142
def usage(self, path_to_model, *args, **kwargs):
100143
"""Shows where model elements are used in an Aspect.
@@ -111,7 +154,7 @@ def usage(self, path_to_model, *args, **kwargs):
111154
# Show usage for an element URN with models root
112155
samm_cli.usage("urn:samm:org.eclipse.example:1.0.0#MyElement", models_root="/path/to/models")
113156
"""
114-
self._call_function("usage", path_to_model, *args, **kwargs)
157+
self._call_function(self.CMD_USAGE, path_to_model, *args, **kwargs)
115158

116159
def to_openapi(self, path_to_model, *args, **kwargs):
117160
"""Generate OpenAPI specification for an Aspect Model.
@@ -136,7 +179,7 @@ def to_openapi(self, path_to_model, *args, **kwargs):
136179
- language, l: the language from the model for which an OpenAPI specification should be generated (default: en)
137180
custom-resolver: use an external resolver for the resolution of the model elements
138181
"""
139-
self._call_function("to openapi", path_to_model, *args, **kwargs)
182+
self._call_function(self.CMD_TO_OPENAPI, path_to_model, *args, **kwargs)
140183

141184
def to_schema(self, path_to_model, *args, **kwargs):
142185
"""Generate JSON schema for an Aspect Model.
@@ -147,7 +190,7 @@ def to_schema(self, path_to_model, *args, **kwargs):
147190
- language, -l: the language from the model for which a JSON schema should be generated (default: en)
148191
- custom-resolver: use an external resolver for the resolution of the model elements
149192
"""
150-
self._call_function("to schema", path_to_model, *args, **kwargs)
193+
self._call_function(self.CMD_TO_SCHEMA, path_to_model, *args, **kwargs)
151194

152195
def to_json(self, path_to_model, *args, **kwargs):
153196
"""Generate example JSON payload data for an Aspect Model.
@@ -157,7 +200,7 @@ def to_json(self, path_to_model, *args, **kwargs):
157200
- output, -o: output file path (default: stdout)
158201
- custom-resolver: use an external resolver for the resolution of the model elements
159202
"""
160-
self._call_function("to json", path_to_model, *args, **kwargs)
203+
self._call_function(self.CMD_TO_JSON, path_to_model, *args, **kwargs)
161204

162205
def to_html(self, path_to_model, *args, **kwargs):
163206
"""Generate HTML documentation for an Aspect Model.
@@ -169,7 +212,7 @@ def to_html(self, path_to_model, *args, **kwargs):
169212
- language, -l: the language from the model for which the HTML should be generated (default: en)
170213
- custom-resolver: use an external resolver for the resolution of the model elements
171214
"""
172-
self._call_function("to html", path_to_model, *args, **kwargs)
215+
self._call_function(self.CMD_TO_HTML, path_to_model, *args, **kwargs)
173216

174217
def to_png(self, path_to_model, *args, **kwargs):
175218
"""Generate PNG diagram for Aspect Model.
@@ -182,7 +225,7 @@ def to_png(self, path_to_model, *args, **kwargs):
182225
- language, -l: the language from the model for which the diagram should be generated (default: en)
183226
- custom-resolver: use an external resolver for the resolution of the model elements
184227
"""
185-
self._call_function("to png", path_to_model, *args, **kwargs)
228+
self._call_function(self.CMD_TO_PNG, path_to_model, *args, **kwargs)
186229

187230
def to_svg(self, path_to_model, *args, **kwargs):
188231
"""Generate SVG diagram for Aspect Model.
@@ -193,7 +236,7 @@ def to_svg(self, path_to_model, *args, **kwargs):
193236
- language, -l: the language from the model for which the diagram should be generated (default: en)
194237
- custom-resolver: use an external resolver for the resolution of the model elements
195238
"""
196-
self._call_function("to svg", path_to_model, *args, **kwargs)
239+
self._call_function(self.CMD_TO_SVG, path_to_model, *args, **kwargs)
197240

198241
def to_java(self, path_to_model, *args, **kwargs):
199242
"""Generate Java classes from an Aspect Model.
@@ -216,7 +259,7 @@ def to_java(self, path_to_model, *args, **kwargs):
216259
- name_prefix, namePrefix: name prefix for generated Aspect, Entity Java classes
217260
- name_postfix, namePostfix: name postfix for generated Aspect, Entity Java classes
218261
"""
219-
self._call_function("to java", path_to_model, *args, **kwargs)
262+
self._call_function(self.CMD_TO_JAVA, path_to_model, *args, **kwargs)
220263

221264
def to_asyncapi(self, path_to_model, *args, **kwargs):
222265
"""Generate AsyncAPI specification for an Aspect Model.
@@ -233,7 +276,7 @@ def to_asyncapi(self, path_to_model, *args, **kwargs):
233276
- separate_files, sf: create separate files for each schema (use as flag without value)
234277
- custom_resolver: use an external resolver for the resolution of the model elements
235278
"""
236-
self._call_function("to asyncapi", path_to_model, *args, **kwargs)
279+
self._call_function(self.CMD_TO_ASYNCAPI, path_to_model, *args, **kwargs)
237280

238281
def to_jsonld(self, path_to_model, *args, **kwargs):
239282
"""Generate JSON-LD representation of an Aspect Model.
@@ -243,7 +286,7 @@ def to_jsonld(self, path_to_model, *args, **kwargs):
243286
- output, o: output file path (default: stdout)
244287
- custom_resolver: use an external resolver for the resolution of the model elements
245288
"""
246-
self._call_function("to jsonld", path_to_model, *args, **kwargs)
289+
self._call_function(self.CMD_TO_JSONLD, path_to_model, *args, **kwargs)
247290

248291
def to_sql(self, path_to_model, *args, **kwargs):
249292
"""Generate SQL script that sets up a table for data for this Aspect.
@@ -288,19 +331,19 @@ def to_sql(self, path_to_model, *args, **kwargs):
288331
# or call the CLI directly. Current implementation supports single custom column.
289332
samm_cli.to_sql("AspectModel.ttl", custom_column=("column1 STRING", "column2 INT"))
290333
"""
291-
self._call_function("to sql", path_to_model, *args, **kwargs)
292-
293-
def to_aas(self, path_to_model, *args, **kwargs):
294-
"""Generate an Asset Administration Shell (AAS) submodel template from an Aspect Model.
295-
296-
param path_to_model: local path to the aspect model file (*.ttl)
297-
possible arguments:
298-
- output, o: output file path (default: stdout)
299-
- format, f: output file format (XML, JSON, or AASX, default: XML)
300-
- aspect_data, a: path to a JSON file containing aspect data corresponding to the Aspect Model
301-
- custom_resolver: use an external resolver for the resolution of the model elements
302-
"""
303-
self._call_function("to aas", path_to_model, *args, **kwargs)
334+
self._call_function(self.CMD_TO_SQL, path_to_model, *args, **kwargs)
335+
336+
# def to_aas(self, path_to_model, *args, **kwargs): # FIXME: https://github.com/eclipse-esmf/esmf-sdk/issues/802
337+
# """Generate an Asset Administration Shell (AAS) submodel template from an Aspect Model.
338+
#
339+
# param path_to_model: local path to the aspect model file (*.ttl)
340+
# possible arguments:
341+
# - output, o: output file path (default: stdout)
342+
# - format, f: output file format (XML, JSON, or AASX, default: XML)
343+
# - aspect_data, a: path to a JSON file containing aspect data corresponding to the Aspect Model
344+
# - custom_resolver: use an external resolver for the resolution of the model elements
345+
# """
346+
# self._call_function(self.CMD_AAS_TO_ASPECT, path_to_model, *args, **kwargs)
304347

305348
def edit_move(self, path_to_model, element, namespace=None, *args, **kwargs):
306349
"""Move a model element definition from its current place to another existing or
@@ -334,7 +377,7 @@ def edit_move(self, path_to_model, element, namespace=None, *args, **kwargs):
334377
samm_cli.edit_move("AspectModel.ttl", "MyAspect newFile.ttl", None, "copy-file-header", "force")
335378
"""
336379
# Build the function name with positional arguments
337-
function_name = f"edit move {element}"
380+
function_name = f"{self.CMD_EDIT_MOVE} {element}"
338381
if namespace:
339382
function_name += f" {namespace}"
340383

@@ -380,7 +423,7 @@ def edit_newversion(self, path_to_model, version_type=None, *args, **kwargs):
380423
if version_type and version_type in ("major", "minor", "micro"):
381424
args_list.insert(0, version_type)
382425

383-
self._call_function("edit newversion", path_to_model, *args_list, **kwargs)
426+
self._call_function(self.CMD_EDIT_NEWVERSION, path_to_model, *args_list, **kwargs)
384427

385428
def aas_to_aspect(self, aas_file, *args, **kwargs):
386429
"""Translate Asset Administration Shell (AAS) Submodel Templates to Aspect Models.
@@ -408,7 +451,7 @@ def aas_to_aspect(self, aas_file, *args, **kwargs):
408451
# or using short form
409452
samm_cli.aas_to_aspect("AssetAdminShell.aasx", s=["1", "2"])
410453
"""
411-
self._call_function("to aspect", aas_file, *args, command_type="aas", **kwargs)
454+
self._call_function(self.CMD_AAS_TO_ASPECT, aas_file, *args, command_type=self.TYPE_AAS, **kwargs)
412455

413456
def aas_list(self, aas_file, *args, **kwargs):
414457
"""Retrieve a list of submodel templates contained within the provided Asset Administration Shell (AAS) file.
@@ -419,7 +462,7 @@ def aas_list(self, aas_file, *args, **kwargs):
419462
# List all submodel templates in an AAS file
420463
samm_cli.aas_list("AssetAdminShell.aasx")
421464
"""
422-
self._call_function("list", aas_file, *args, command_type="aas", **kwargs)
465+
self._call_function(self.CMD_AAS_LIST, aas_file, *args, command_type=self.TYPE_AAS, **kwargs)
423466

424467
def package_import(self, namespace_package, *args, **kwargs):
425468
"""Imports a Namespace Package (file or URL) into a given models' directory.
@@ -446,7 +489,7 @@ def package_import(self, namespace_package, *args, **kwargs):
446489
# Force import
447490
samm_cli.package_import("MyPackage.zip", "force", models_root="c:\\models")
448491
"""
449-
self._call_function("import", namespace_package, *args, command_type="package", **kwargs)
492+
self._call_function(self.CMD_PACKAGE_IMPORT, namespace_package, *args, command_type=self.TYPE_PACKAGE, **kwargs)
450493

451494
def package_export(self, model_or_urn, *args, **kwargs):
452495
"""Exports an Aspect Model with its dependencies or a complete namespace to a Namespace Package (.zip).
@@ -473,4 +516,4 @@ def package_export(self, model_or_urn, *args, **kwargs):
473516
# Export to specific location
474517
samm_cli.package_export("AspectModel.ttl", output="c:\\exports\\my-package.zip")
475518
"""
476-
self._call_function("export", model_or_urn, *args, command_type="package", **kwargs)
519+
self._call_function(self.CMD_PACKAGE_EXPORT, model_or_urn, *args, command_type=self.TYPE_PACKAGE, **kwargs)

core/esmf-aspect-meta-model-python/scripts/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ class TestModelConstants:
3434
"esmf-test-aspect-models-$version_number.jar"
3535
)
3636
SAMM_VERSION = SAMM_VERSION
37-
TEST_MODELS_PATH = join("tests", "integration", "java_models", "resources")
37+
TEST_MODELS_PATH = join("tests", "integration", "pymodels", "java_models", "resources")

core/esmf-aspect-meta-model-python/tests/integration/java_models/__init__.py renamed to core/esmf-aspect-meta-model-python/tests/functional/__init__.py

File renamed without changes.

0 commit comments

Comments
 (0)