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
1311import subprocess
1412
1513from 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 )
0 commit comments