-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SAMM cli usage #33
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,155 @@ | ||||||
# Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH | ||||||
# | ||||||
# See the AUTHORS file(s) distributed with this work for additional | ||||||
# information regarding authorship. | ||||||
# | ||||||
# This Source Code Form is subject to the terms of the Mozilla Public | ||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||||||
# | ||||||
# SPDX-License-Identifier: MPL-2.0 | ||||||
|
||||||
import subprocess | ||||||
|
||||||
from os.path import exists, join | ||||||
from pathlib import Path | ||||||
|
||||||
from scripts.download_samm_cli import download_samm_cli | ||||||
|
||||||
|
||||||
class SammCli: | ||||||
"""Class to eecute SAMM CLI functions. | ||||||
|
||||||
If there is no downloaded SAMM CLI, code will identify operation system and download a corresponding client. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed |
||||||
""" | ||||||
|
||||||
def __init__(self): | ||||||
self._samm = self._get_client_path() | ||||||
self._validate_client() | ||||||
|
||||||
@staticmethod | ||||||
def _get_client_path(): | ||||||
"""Get path to the SAMM Cli executable file..""" | ||||||
base_path = Path(__file__).resolve() | ||||||
cli_path = join(base_path.parents[1], "samm-cli", "samm.exe") | ||||||
|
||||||
return cli_path | ||||||
|
||||||
def _validate_client(self): | ||||||
"""Validate SAMM Cli. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please unify the name, e.g. use "SAMM CLI" everywhere There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed for all comments |
||||||
|
||||||
If there is no SAMM Cli executable file, run a script for downloading. | ||||||
""" | ||||||
if not exists(self._samm): | ||||||
# download SAMM CLI | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this comment, this is redundant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||||||
download_samm_cli() | ||||||
|
||||||
def _call_function(self, function_name, path_to_model, *args, **kwargs): | ||||||
"""Run a SAMM Cli function as a subprocess.""" | ||||||
call_args = [self._samm, "aspect", path_to_model] + function_name.split() | ||||||
|
||||||
if args: | ||||||
call_args.extend([f"-{param}" for param in args]) | ||||||
|
||||||
if kwargs: | ||||||
for key, value in kwargs.items(): | ||||||
if len(key) == 1: | ||||||
arg = f"-{key}={value}" | ||||||
else: | ||||||
key = key.replace("_", "-") | ||||||
arg = f"--{key}={value}" | ||||||
|
||||||
call_args.append(arg) | ||||||
|
||||||
subprocess.run(call_args, shell=True, check=True) | ||||||
|
||||||
def validate(self, path_to_model, *args, **kwargs): | ||||||
"""Validate Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("validate", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_openapi(self, path_to_model, *args, **kwargs): | ||||||
"""Generate OpenAPI specification for an Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, o: output file path (default: stdout) | ||||||
- api-base-url, b: the base url for the Aspect API used in the OpenAPI specification, b="http://mysite.de" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please something neutral as example, e.g. http://example.com/ or http://localhost/. http://mysite.de is an actually existing website. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated. |
||||||
- json, j: generate a JSON specification for an Aspect Model (default format is YAML) | ||||||
- comment, c: only in combination with --json; generates $comment OpenAPI 3.1 keyword for all | ||||||
samm:see attributes | ||||||
- parameter-file, p: the path to a file including the parameter for the Aspect API endpoints. | ||||||
For detailed description, please have a look at a SAMM CLI documentation (https://eclipse-esmf.github.io/esmf-developer-guide/tooling-guide/samm-cli.html#using-the-cli-to-create-a-json-openapi-specification) # noqa: E501 | ||||||
- semantic-version, sv: use the full semantic version from the Aspect Model as the version for the Aspect API | ||||||
- resource-path, r: the resource path for the Aspect API endpoints | ||||||
For detailed description, please have a look at a SAMM CLI documentation (https://eclipse-esmf.github.io/esmf-developer-guide/tooling-guide/samm-cli.html#using-the-cli-to-create-a-json-openapi-specification) # noqa: E501 | ||||||
- include-query-api, q: include the path for the Query Aspect API Endpoint in the OpenAPI specification | ||||||
- paging-none, pn: exclude paging information for the Aspect API Endpoint in the OpenAPI specification | ||||||
- paging-cursor-based, pc: in case there is more than one paging possibility, it must be cursor based paging | ||||||
- paging-offset-based, po: in case there is more than one paging possibility, it must be offset based paging | ||||||
- paging-time-based, pt: in case there is more than one paging possibility, it must be time based paging | ||||||
- language, l: the language from the model for which an OpenAPI specification should be generated (default: en) | ||||||
custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to openapi", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_schema(self, path_to_model, *args, **kwargs): | ||||||
"""Generate JSON schema for an Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, -o: output file path (default: stdout) | ||||||
- language, -l: the language from the model for which a JSON schema should be generated (default: en) | ||||||
- custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to schema", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_json(self, path_to_model, *args, **kwargs): | ||||||
"""Generate example JSON payload data for an Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, -o: output file path (default: stdout) | ||||||
- custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to json", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_html(self, path_to_model, *args, **kwargs): | ||||||
"""Generate HTML documentation for an Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, -o: the output will be saved to the given file | ||||||
- css, -c: CSS file with custom styles to be included in the generated HTML documentation | ||||||
- language, -l: the language from the model for which the HTML should be generated (default: en) | ||||||
- custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to html", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_png(self, path_to_model, *args, **kwargs): | ||||||
"""Generate PNG diagram for Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, -o: output file path (default: stdout); | ||||||
as PNG is a binary format, it is strongly recommended to output the result to a file | ||||||
by using the -o option or the console redirection operator '>' | ||||||
- language, -l: the language from the model for which the diagram should be generated (default: en) | ||||||
- custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to png", path_to_model, *args, **kwargs) | ||||||
|
||||||
def to_svg(self, path_to_model, *args, **kwargs): | ||||||
"""Generate SVG diagram for Aspect Model. | ||||||
|
||||||
param path_to_model: local path to the aspect model file (*.ttl) | ||||||
possible arguments: | ||||||
- output, -o: the output will be saved to the given file | ||||||
- language, -l: the language from the model for which the diagram should be generated (default: en) | ||||||
- custom-resolver: use an external resolver for the resolution of the model elements | ||||||
""" | ||||||
self._call_function("to svg", path_to_model, *args, **kwargs) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
"""Download SAMM CLI. | ||
|
||
Windows: https://github.com/eclipse-esmf/esmf-sdk/releases/download/v2.6.1/samm-cli-2.6.1-windows-x86_64.zip | ||
Linux: https://github.com/eclipse-esmf/esmf-sdk/releases/download/v2.6.1/samm-cli-2.6.1-linux-x86_64.tar.gz | ||
JAR: https://github.com/eclipse-esmf/esmf-sdk/releases/download/v2.6.1/samm-cli-2.6.1.jar | ||
""" | ||
|
||
import os | ||
from pathlib import Path | ||
import platform | ||
import requests | ||
import sys | ||
import zipfile | ||
|
||
BASE_PATH = "https://github.com/eclipse-esmf/esmf-sdk/releases/download/v2.6.1/" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it would make sense to define the version string ("2.6.1") as a variable or constant, since it's repeated many times over. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a separate constant for the version number There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps it's worth extracting the version of CLI to config, or at least to a constant? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use a constant for the SAMM CLI version |
||
|
||
|
||
def get_samm_cli_file_name(): | ||
"""Get a SAMM CLI file name for the current platform.""" | ||
|
||
if platform.system() == "Windows": | ||
file_name = "samm-cli-2.6.1-windows-x86_64.zip" | ||
elif platform.system() == "Linux": | ||
file_name = "samm-cli-2.6.1-linux-x86_64.tar.gz" | ||
else: | ||
raise NotImplementedError(f"Please download a SAMM CLI manually for your operation system from '{BASE_PATH}'") | ||
|
||
return file_name | ||
|
||
|
||
def download_archive_file(url, archive_file): | ||
"""Download an archive file.""" | ||
with open(archive_file, "wb") as f: | ||
print("Downloading %s" % archive_file) | ||
response = requests.get(url, allow_redirects=True, stream=True) | ||
content_len = response.headers.get('content-length') | ||
|
||
if content_len is None: | ||
f.write(response.content) | ||
else: | ||
total_len = int(content_len) | ||
data_len = 0 | ||
chunk = 4096 | ||
progress_bar_len = 50 | ||
|
||
for content_data in response.iter_content(chunk_size=chunk): | ||
data_len += len(content_data) | ||
|
||
f.write(content_data) | ||
|
||
curr_progress = int(50 * data_len / total_len) | ||
sys.stdout.write(f"\r[{'*' * curr_progress}{' ' * (progress_bar_len - curr_progress)}]") | ||
sys.stdout.flush() | ||
|
||
|
||
def download_samm_cli(): | ||
try: | ||
samm_cli_file_name = get_samm_cli_file_name() | ||
except NotImplementedError as error: | ||
print(error) | ||
else: | ||
print(f"Start downloading SAMM CLI {samm_cli_file_name}") | ||
url = BASE_PATH + samm_cli_file_name | ||
dir_path = Path(__file__).resolve().parent | ||
archive_file = os.path.join(dir_path, f".\\{samm_cli_file_name}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you have to use: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
|
||
download_archive_file(url, archive_file) | ||
print("SAMM CLI archive file downloaded") | ||
|
||
print("Start extracting files") | ||
archive = zipfile.ZipFile(archive_file) | ||
for file in archive.namelist(): | ||
archive.extract(file, ".\\samm-cli") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work on all platforms (because of the backslash There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaces with os specific separator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you have to use, also here something like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
archive.close() | ||
print("Done extracting files.") | ||
|
||
print("Deleting SAMM CLI archive file.") | ||
os.remove(archive_file) | ||
|
||
|
||
if __name__ == "__main__": | ||
download_samm_cli() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed