SAMM CLI
The SAMM CLI is a command line tool for the validation of Aspect Models and the generation of artifacts, such as documentation or code, from Aspect Models. It is available as a native executable for multiple platforms (Windows, Linux and macOS) and as an executable JAR file. You can find the latest release [on GitHub](https://github.com/eclipse-esmf/esmf-sdk/releases/latest).
Running the SAMM CLI
For the executable jar, call java -jar samm-cli-2.9.8.jar
followed by one of the
following subcommands (e.g., java -jar samm-cli-2.9.8.jar help
). In the following
sections, samm
will be used as the command name.
To make
|
-
For the Windows native executable:
-
Download: Download via above link or visit the repository of the Java SDK which contains the SAMM CLI at the Github releases page and download executable file for Windows.
-
Extract: extract it to a location of your choice.
-
Open
samm.exe
: This will open a command prompt in this directory, where you can enter furthersamm
commands. -
Input: Make sure to read the below documentation and provide model files in the correct directory structure.
-
-
For the Linux native executable:
-
Download: Download via above link or visit the repository of the Java SDK which contains the SAMM CLI at the Github releases page and download executable file for Linux.
-
Extract: Unpack the archive to your preferred directory.
-
Open Terminal: Open the Terminal and navigate to the directory with the application.
-
Input: Make sure to read the below documentation and provide model files in the correct directory structure.
-
Execute: Run the application using the command
./samm
.
-
Successful execution of a command is signaled by returning 0. In case of a logical or other internal error the error code 1 is being returned. Missing or wrong command parameters result in error code 2 being returned.
To get an overview of all commands and subcommands, use samm help
. To get help for a certain
subcommand, use help
before the subcommand name, e.g., samm help aspect
, samm help aspect
validate
or samm help aas to aspect
. Each subcommand can have its own set of options which allow
the user to further fine-tune the execution of the command. The available options and their meaning
can also be seen in the help text of the individual subcommands.
List of commands
In place of
|
Command | Description/Options | Examples |
---|---|---|
help |
Get overview of all commands |
|
Get help for a list of subcommands |
|
|
|
||
|
||
Validate Aspect Model |
|
|
--custom-resolver : use an external resolver for the resolution of the model elements |
|
|
Pretty-print Aspect Model |
|
|
--output, -o : the output will be saved to the given file |
|
|
--overwrite, -w : Overwrite the input file |
|
|
Generate HTML documentation for an Aspect Model |
|
|
--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 |
|
|
Generate PNG diagram for Aspect Model |
|
|
--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 |
|
|
Generate SVG diagram for Aspect Model |
|
|
--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 |
|
|
Generate Java classes from an Aspect Model |
|
|
--output-directory, -d : output directory to write files to (default: current directory) |
||
--package-name, -pn : package to use for generated Java classes |
|
|
--no-jackson, -nj : disable Jackson annotation generation in generated Java classes |
||
--json-type-info, -jti : If Jackson annotations are enabled, determines the value of JsonTypeInfo.Id. Default: DEDUCTION |
||
--template-library-file, -tlf : the path and name of the Velocity template file containing the macro library |
||
--execute-library-macros, -elm : Execute the macros provided in the Velocity macro library |
||
--static, -s : generate Java domain classes for a Static Meta Model |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
--name-prefix, -namePrefix : name prefix for generated Aspect, Entity Java classes |
|
|
--name-postfix, -namePostfix : name postfix for generated Aspect, Entity Java classes |
|
|
Generate OpenAPI specification for an Aspect Model |
|
|
--output, -o : output file path (default: stdout) |
||
--api-base-url, -b : the base url for the Aspect API used in the OpenAPI specification |
|
|
--json, -j : generate a JSON specification for an Aspect Model (default format is YAML) |
||
--comment, -c : only in combination with --json; generates |
||
--parameter-file, -p : the path to a file including the parameter for the Aspect API endpoints |
For detailed description, see the section bellow |
|
--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, see the section bellow |
|
--include-query-api, -q : include the path for the Query Aspect API Endpoint in the OpenAPI specification |
||
--include-crud, -cr : include the POST/PUT/PATCH methods in the OpenAPI specification |
||
--include-post, -post : include the POST method in the OpenAPI specification |
||
--include-put, -put : include the PUT method in the OpenAPI specification |
||
--include-patch, -patch : include the PATCH method 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) |
|
|
--template-file, -t : the path to a file including a template for the resulting specification, can be in JSON or YAML |
For detailed description, see the section bellow |
|
--separate-files, -sf : Create separate files for each schema |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
Generate AsyncAPI specification for an Aspect Model |
|
|
--output, -o : output file path (default: stdout) |
||
--channel-address, -ca : Sets the channel address (i.e., for MQTT, the topic’s name). OpenAPI specification |
|
|
--application-id, -ai : Sets the application id, e.g. an identifying URL. |
||
--semantic-version, -sv : use the full semantic version from the Aspect Model as the version for the Aspect API |
||
--language, -l : The language from the model for which an AsyncAPI specification should be generated (default: en) |
|
|
--separate-files, -sf : Create separate files for each schema |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
Generate example JSON payload data for an Aspect Model |
|
|
--output, -o : output file path (default: stdout) |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
--add-type-attribute, -ta : Add |
||
Generate JSON-LD representation of an Aspect Model |
|
|
--output, -o : output file path (default: stdout) |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
Generate JSON schema for an Aspect Model |
|
|
--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 |
||
Generate SQL script that sets up a table for data for this Aspect |
|
|
--output, -o : output file path (default: stdout) |
||
--language, -l : The language from the model to use for generated comments |
||
--dialect, -d : The SQL dialect to generate for (default: |
||
--mapping-strategy, -s : The mapping strategy to use (default: |
||
--include-table-comment, -tc : Include table comment in the generated SQL script
(default: |
||
--include-column-comments, -cc : Include column comments in the generated SQL
script (default: |
||
--table-command-prefix, -tcp : The prefix to use for Databricks table creation
commands (default: |
||
--decimal-precision, -dp : The precision to use for Databricks decimal columns (default: 10). See also notes in the Databricks type mapping. |
||
--custom-column, -col : Additional custom column definition, e.g. for databricks following the pattern |
|
|
Generate an Asset Administration Shell (AAS) submodel template from an Aspect Model |
|
|
--output, -o : output file path (default: stdout) |
||
--format, -f : output file format (XML, JSON, or AASX, default: XML) |
||
--custom-resolver : use an external resolver for the resolution of the model elements |
||
--aspect-data, -a : path to a JSON file containing aspect data corresponding to the Aspect Model |
||
Move a model element definition from its current place to another existing or new file in the same or another namespace. |
|
|
--dry-run : Don’t write changes to the file system, but print a report of changes that would be performed. |
||
--details : When used with |
||
--copy-file-header : When a model element is moved to a new file, copy the file header from the source file to the new file |
||
--force : When a new file is to be created but it already exists in the file system,
the operation will be cancelled, unless |
||
aspect <model> edit newversion [--major | --minor | --micro] |
Create a new version of an existing file or a complete
namespace. |
|
--major : Update the major version |
||
--minor : Update the minor version |
||
--micro : Update the micro version |
||
--dry-run : Don’t write changes to the file system, but print a report of changes that would be performed. |
||
--details : When used with |
||
--force : When a new file is to be created but it already exists in the file system,
the operation will be cancelled, unless |
||
Shows where model elements are used in an Aspect. |
|
|
Translate Asset Administration Shell (AAS) Submodel Templates to Aspect Models |
|
|
--output-directory, -d : output directory to write files to (default: current directory) |
||
--submodel-template, -s : selected submodel template for generating;
run |
|
|
Retrieve a list of submodel templates contained within the provided Asset Administration Shell (AAS) file. |
|
Configuration of model resolution
When a loaded file refers to model elements defined elsewhere, the model resolver looks up the files to load. You can configure where such lookup should be done:
-
Using the
--models-root
switch, you can provide a directory that follows the models directory structure. This switch can be repeated to provide multiple models root directories. -
Using the
--custom-resolver
switch, you can provide a custom resolver implementation. The value of the switch can be any command which is directly executable by the underlying operating system (such as a batch script on Windows or a shell script on Linux/Unix). When a model element needs to be resolved, this command is executed with the URN of the element to resolve passed as the last parameter. The command can provide other parameters as well, the element URN will be added automatically as the last one by samm-cli. The resolved model definition is expected to be output to the stdout in Turtle format. In this way the extension can be flexibly done in any programming language/script language, including complex logic if necessary. -
Using the
--github
switch, you can configure a location in a remote GitHub repository as models root, e.g.,eclipse-esmf/esmf-sdk
. Optionally, you can also provide--github-directory
to set the remote directory and--github-branch
or--github-tag
to set the branch name or tag, respectively. If the repository is private, you can also pass--github-token
and set a fine-grained access token; the token needs at least "Contents" repository read permissions.
When using an Aspect Model URN as input to a command (such as
urn:samm:org.eclipse.esmf.example:1.0.0#Aspect ), you must also provide at least one of the
switches mentioned above (--models-root , --custom-resolver or --github ), otherwise samm-cli
will not know where to find the corresonding file(s).
|
Using the CLI to create a JSON OpenAPI Specification
Every specification is based on one Aspect, which needs a separately defined server URL where the given aspect will be.
The URL will be defined as string with the -b
option, i.e.: https://www.example.org. The default URL, using the above
defined --api-base-url
, would result in https://www.example.org/api/v1/{tenantId}/<aspectName>. By default,
{tenantId}
followed by the Aspect’s name is used as path, with the aspect name converted from CamelCase to
kebab-case. The default path can be changed with the --resource-path
switch. If the path is defined further, for
example using --resource-path "/resources/{resourceId}"
, the resulting URL would be:
https://www.example.org/resources/{resourceId}.
It will be required to specify the parameter, in case there is an additional parameter defined. This has to be done in
JSON or in YAML, depending on the kind of specification chosen. For example: With the option --resource-path
"/resources/{resourceId}"
the generator constructs the URL https://www.example.org/resources/{resourceId} and then
the --parameter-file
defines the parameter resourceId in YAML:
resourceId:
name: resourceId
in: path
description: An example resource Id.
required: true
schema:
type: string
Definitions of the parameters must correspond to the specification of the OpenAPI Parameter Object.
A template can be used for more specific customization - parameter --template-file
.
The template is a document in the OpenAPI format, with one crucial difference - the presence of a templated Paths Object paths.__DEFAULT_QUERIES_TEMPLATE__
.
All values not defined by the generator will be taken from the template.
For generator-defined Aspect Model resources, the values can be taken from the corresponding Operation Object template paths.__DEFAULT_QUERIES_TEMPLATE__
.
For example, the template file could look like this:
info:
termsOfService: 'https://example.com/terms-of-service'
servers:
- url: https://{environment}.example.com/api/v1
variables:
environment:
default: api
enum:
- api
- sandbox-api
components:
responses:
InternalServerError:
description: An error occurred while processing the request.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
paths:
/status:
get:
tags:
- Maintenance
operationId: getHealthStatus
description: Check the health status of the service.
responses:
"200":
description: The service is up and running.
"503":
description: The service is down.
__DEFAULT_QUERIES_TEMPLATE__:
get:
parameters:
- name: request-id
in: header
description: The unique identifier for the request. If it isn't provided, it will be auto-generated.
required: false
schema:
type: string
format: uuid
responses:
"500":
$ref: '#/components/responses/InternalServerError'
post:
parameters:
- name: request-id
in: header
description: The unique identifier for the request. If it isn't provided, it will be auto-generated.
required: false
schema:
type: string
format: uuid
The full command for the native executable samm-cli would be:
samm aspect AspectModel.ttl to openapi -b "https://www.example.org" -r "/resources/{resourceId}" -p fileLocation
For the Java version of samm-cli, the full command would result in:
java -jar samm-cli-2.9.8.jar aspect AspectModel.ttl to openapi -b "https://www.example.org" -r "/resources/{resourceId}" -p fileLocation
Configuration of security schemas
For example, to configure OAuth2 authentication the template could look like this:
security:
- OAuth2:
- read:aspects
components:
securitySchemes:
OAuth2:
type: oauth2
flows:
implicit:
authorizationUrl: https://{environment}.example.com/oauth2/authorize
tokenUrl: https://{environment}.example.com/oauth2/token
scopes:
read:aspects: Read access to aspects
write:aspects: Write access to aspects
paths:
__DEFAULT_QUERIES_TEMPLATE__:
post:
security:
- OAuth2:
- read:aspects
- write:aspects
Mapping between the Aspect Models and the OpenAPI Specification
In this section, a detailed description of the mapping between individual Aspect elements and the OpenAPI specification is given. To make it easier to follow, the mapping is explained based on a concrete example, divided into logically coherent blocks. Please bear in mind that these blocks are snippets or fragments of a larger whole; viewed in isolation they do not necessarily form a valid or meaningful Aspect Model or OpenAPI specification.
Naming and versioning
Please consider the following model fragment, with the attention focused on the numbered elements:
@prefix : <urn:samm:com.mycompany.myapplication:1.0.0#> . (1)
@prefix samm: <urn:samm:org.eclipse.esmf.samm:meta-model:2.1.0#> .
:Test a samm:Aspect; (3)
samm:preferredName "TestAspect"@en ; (2)
samm:preferredName "TestAspekt"@de .
1 | prefix used to build the full URN of :Test Aspect |
2 | the preferred name of the Aspect in language of user’s choice |
3 | the name of the Aspect |
For the generated OpenAPI specification, the following mapping would apply:
{
"openapi" : "3.0.3",
"info" : {
"title" : "TestAspect", (2) (3)
"version" : "v1" (1)
}
}
1 | depending on parameters used when generating the specification, this is either the major version of the full Aspect URN (2.0.0), or it can be the full version (v2.0.0 ), if using -sv (semantic version) command line switch |
2 | if present, samm:preferredName is used as the value for the title element of the specification |
3 | as samm:preferredName is an optional element, in cases when it is missing the name of the Aspect is used instead |
The version information as described above is also used in the URL definitions of the servers
block of the specification:
{
"servers" : [ {
"url" : "http://example.com/api/v1", (1)
"variables" : {
"api-version" : {
"default" : "v1" (1)
}
}
} ]
}
The name of the Aspect is used to generate several important OpenAPI artifacts, like the path definitions for the API:
{
"paths" : {
"/{tenant-id}/test" : { (3)
"get" : {
"tags" : [ "Test" ], (3)
"operationId" : "getTest" (3)
}
}
}
}
and the definitions for request bodies and responses in the corresponding blocks (requestBodies
and responses
) of the OpenAPI specification (example omitted for simplicity).
Mapping of Aspect and its properties
For each Aspect in the model, an entry in the components/schemas
part of the OpenAPI specification is generated.
For an example Aspect from the following fragment:
:Test a samm:Aspect; (1)
samm:properties (
:prop1 (2)
[ samm:property :prop2; samm:payloadName "givenName"; ] (3)
[ samm:property :prop3; samm:optional true; ] ). (4)
:prop1 a samm:Property;
samm:description "Description of Property1"@en; (5)
samm:characteristic :Enum. (6)
an entry like the one given in the following JSON will be generated:
"Test" : { (1)
"type" : "object",
"properties" : {
"prop1" : { (2)
"description" : "Description of Property1", (5)
"$ref" : "#/components/schemas/urn_samm_test_2.0.0_Enum" (6)
},
"givenName" : { (3)
"$ref" : "#/components/schemas/urn_samm_test_2.0.0_EntityChar"
},
"prop3" : { (4)
"$ref" : "#/components/schemas/urn_samm_test_2.0.0_StringCharacteristic"
}
},
"required" : [ "prop1", "givenName" ] (2) (3)
}
1 | the name of the Aspect is used to name the schema object for the aspect |
2 | with plain property references, the name of the property is used to name the property definition |
3 | in cases where a payload name is defined on a specific property, it is used in preference to the plain property name |
4 | if the property use is also defined as optional, the property will not be included in the list of the required properties |
5 | the values of samm:description elements in property definitions are included in the generated JSON |
6 | for each of the properties characteristics an entry in components/schemas is generated and referenced here; if the characteristic is of complex type, the whole procedure is applied recursively to the complex type’s properties |
Mapping of Aspect’s operations
If the Aspect also has a non-empty list of operations defined, like the one in the following example:
:AspectWithOperation a samm:Aspect ;
samm:properties ( ) ;
samm:operations ( :testOperation ) .
:testOperation a samm:Operation ;
samm:input ( :input ) ; (1)
samm:output :output . (2)
:output a samm:Property ;
samm:characteristic samm-c:Text . (3)
:input a samm:Property ;
samm:characteristic samm-c:Text . (4)
then additional entries are added to the generated OpenAPI specification.
First, there is an additional entry in the paths
section of the specification: /{tenant-id}/aspect-with-operation/operations
.
The available operations are then added to the components/schemas
part:
{
"Operation" : {
"allOf" : [ {
"$ref" : "#/components/schemas/JsonRpc"
}, {
"properties" : {
"params" : {
"type" : "object",
"required" : [ "input" ], (1)
"properties" : {
"input" : { (1)
"$ref" : "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.0.0_Text" (3)
}
}
},
"method" : {
"type" : "string",
"description" : "The method name",
"example" : "testOperation"
}
}
} ]
},
"OperationResponse" : {
"allOf" : [ {
"$ref" : "#/components/schemas/JsonRpc"
}, {
"properties" : {
"result" : {
"type" : "object",
"required" : [ "output" ], (2)
"properties" : {
"output" : { (2)
"$ref" : "#/components/schemas/urn_samm_org.eclipse.esmf.samm_characteristic_2.0.0_Text" (4)
}
}
}
}
} ]
}
}
1 | the names of the input |
2 | and output parameters are reflected in the properties generated for the request/response objects |
3 | the characteristics are generated |
4 | and referenced as described in the point 6 of the section "Mapping of Aspect and its properties" |
As usual, corresponding entries referencing the definitions above are added to the requestBodies
and responses
sections (examples omitted for simplicity).
For technical reasons, there may be a slight variation in the generated JSON depending on whether the aspect has one or more operations defined.
Mapping of Collections
There are some additional JSON entries generated for complex types related to various types of collections to facilitate access to the individual elements of these collections via paging. As these entries are rather of static character without direct references to any aspect elements, it suffices here to give a short overview about which kind of paging is available for which type of collection:
-
a general Collection - cursor and/or offset based paging
-
TimeSeries - cursor, offset and/or time based paging
For all these paging mechanisms, an additional entry with the name PagingSchema
is generated in the components/schemas
part of the specification,
which is then used as the main response schema for the Aspect. Basically, instead of a single Aspect, a collection of Aspects is returned,
together with optional total number of Aspects available in the collection:
"PagingSchema" : {
"type" : "object",
"properties" : {
"items" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/Test"
}
},
"totalItems" : {
"type" : "number"
}
}
}
Depending on the concrete paging model selected, there can be additional properties in the PagingSchema
object.
For cursor based paging, the cursor
object denotes the position of the returned Aspects in relation to some other
uniquely identifiable Aspect (before
or after
it):
"cursor" : {
"type" : "object",
"properties" : {
"before" : {
"type" : "string",
"format" : "uuid"
},
"after" : {
"type" : "string",
"format" : "uuid"
}
}
},
For offset and time based paging, the data is returned in batches of requested size ("pages"), described using the following properties (the meaning of which is self explanatory):
"totalPages" : {
"type" : "number"
},
"pageSize" : {
"type" : "number"
},
"currentPage" : {
"type" : "number"
}
In addition to the PagingSchema
object, also several new parameters are added to the request parameters section of the generated document,
with the help of which the size and/or the relative position of the returned data can be controlled.
All paging mechanisms have the following parameters in common, the meaning of which can be discerned from their descriptions:
{
"name" : "count",
"in" : "query",
"description" : "Number of items to return per call.",
"required" : false,
"schema" : {
"type" : "number"
}
},
{
"name" : "totalItemCount",
"in" : "query",
"description" : "Flag that indicates that the total counts should be returned.",
"required" : false,
"schema" : {
"type" : "boolean"
}
}
Depending on the exact paging model selected, additional paging specific parameters are available. For offset based paging:
"name" : "start",
"in" : "query",
"description" : "Starting index which is starting by 0",
"required" : false,
"schema" : {
"type" : "number"
}
For cursor based paging:
{
"name" : "previous",
"in" : "query",
"description" : "URL to request the previous items. An empty value indicates there are no previous items.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "uri"
}
},{
"name" : "next",
"in" : "query",
"description" : "URL to request the next items. An empty value indicates there are no other items.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "uri"
}
}, {
"name" : "before",
"in" : "query",
"description" : "The cursor that points to the start of the page of items that has been returned.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "uuid"
}
}, {
"name" : "after",
"in" : "query",
"description" : "The cursor that points to the end of items that has been returned.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "uuid"
}
}
And finally for the time based paging:
{
"name" : "since",
"in" : "query",
"description" : "A timestamp that points to the start of the time-based data.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "date-time"
}
}, {
"name" : "until",
"in" : "query",
"description" : "A timestamp that points to the end of the time-based data.",
"required" : false,
"schema" : {
"type" : "string",
"format" : "date-time"
}
}, {
"name" : "limit",
"in" : "query",
"description" : "Number of items to return per call.",
"required" : false,
"schema" : {
"type" : "number"
}
}
Using the CLI to create a JSON AsyncAPI Specification
As with OpenAPI, every AsyncAPI specification is based on one Aspect.
Each Aspect is associated with one channel address.
The channel address is set as string using with`--channel-address` or -ca
option, e.g.: -ca 123-456/789-012/test/1.0.0/Aspect
. The default channel address
is derived from the Aspect’s URN and is constructed using the pattern {namespace}/{version}/{aspectName}
.
The full command for the native executable samm-cli would be:
samm aspect AspectModel.ttl to asyncapi -ca "123-456/789-012/test/1.0.0/Aspect"
For the Java version of samm-cli, the corresponding full command is:
java -jar samm-cli-2.9.8.jar aspect AspectModel.ttl to asyncapi -ca "123-456/789-012/test/1.0.0/Aspect"
Mapping between the Aspect Models and the AsyncAPI Specification
In this section, a detailed description of the mapping between individual Aspect elements and the AsyncAPI specification is given. To make it easier to follow, the mapping is explained based on a concrete example, divided into logically coherent blocks. Please bear in mind that these blocks are snippets or fragments of a larger whole; viewed in isolation they do not necessarily form a valid or meaningful Aspect Model or AsyncAPI specification.
Naming and versioning
Please consider the following model fragment, with the attention focused on the numbered elements:
@prefix : <urn:samm:com.mycompany.myapplication:1.0.0#> . (1)
@prefix samm: <urn:samm:org.eclipse.esmf.samm:meta-model:2.1.0#> .
:Movement a samm:Aspect ; (3)
samm:name "Movement" ;
samm:preferredName "My Movement Aspect"@en ; (2)
samm:description "Aspect for movement information"@en . (4)
1 | prefix used to build the full URN of :Movement Aspect |
2 | the preferred name of the Aspect in the language of the user’s choice |
3 | the name of the Aspect |
4 | the description of the Aspect |
For the generated AsyncAPI specification, the following mapping would apply:
{
"asyncapi" : "3.0.0",
"info" : {
"title" : "My Movement Aspect MQTT API", (2)
"version" : "v1", (1)
"description" : "Aspect for movement information" (3)
}
}
1 | depending on parameters used when generating the specification, this is either the major version of the full Aspect URN (2.0.0), or it can be the full version (v2.0.0 ), if using -sv (semantic version) command line switch |
2 | as samm:preferredName is used as the value for the title element of the specification (MQTT API defined automatically) |
3 | as samm:description is an optional element |
The name of the Aspect is used to generate parts of the AsyncAPI specification, such as the channel definitions for the API:
{
"channels" : {
"Movement" : { (3)
"address" : "movement/0.0.1/Movement", (3)
"description" : "This channel for updating Movement Aspect.", (3)
"parameters" : {
"namespace" : "movement",
"version" : "0.0.1",
"aspect-name" : "Movement" (3)
},
"messages" : {}
}
}
}
Mapping of Aspect’s operations
The AsyncAPI specification is generated based on SAMM Operations and Events. This section describes how specification parts are generated for Operations. If the Aspect also has a non-empty list of Operations defined, such as the one in the following example:
:Movement a samm:Aspect ;
samm:preferredName "movement"@en ;
samm:description "Aspect for movement information"@en ;
samm:properties ( ) ;
samm:operations ( :getSpeed ) ;
samm:events ( ) .
:getSpeed a samm:Operation ;
samm:preferredName "Get speed"@en ;
samm:description "Returns the current speed"@en ;
samm:input ( :getSpeedInput ) ; (1)
samm:output :getSpeedOutput . (2)
:getSpeedOutput a samm:Property ;
samm:preferredName "getSpeed output"@en ;
samm:description "Return value of the getSpeed operation"@en ;
samm:characteristic :OutputCharacteristic .
:OutputCharacteristic a samm:Characteristic ;
samm:preferredName "Output"@en ;
samm:description "Describes the output of the getSpeed operation"@en ;
samm:dataType :OutputEntity .
:OutputEntity a samm:Entity ;
samm:preferredName "Output entity"@en ;
samm:description "The structured response of getSpeed"@en ;
samm:properties ( :outputEntityMessage ) .
:outputEntityMessage a samm:Property ;
samm:characteristic samm-c:Text . (3)
:getSpeedInput a samm:Property ;
samm:preferredName "getSpeed input"@en ;
samm:description "The input to the getSpeed operation"@en ;
samm:characteristic samm-c:Text . (4)
operations will be generated in some places of AsyncAPI specification: operations section, channel messages section and schemas section:
{
"channels" : {
"Movement" : {
"address" : "movement/0.0.1/Movement",
"description" : "This channel for updating Movement Aspect.",
"parameters" : {
"namespace" : "movement",
"version" : "0.0.1",
"aspect-name" : "Movement"
},
"messages" : {
"getSpeedInput" : { (1)
"$ref" : "#/components/messages/getSpeedInput" (1)
},
"getSpeedOutput" : { (2)
"$ref" : "#/components/messages/getSpeedOutput" (2)
}
}
}
},
"operations" : {
"getSpeedInput" : {
"action" : "receive", (5)
"channel" : {
"$ref" : "#/channels/Movement"
},
"messages" : [ {
"$ref" : "#/channels/Movement/messages/getSpeedInput" (1)
} ]
},
"getSpeedOutput" : { (2)
"action" : "send", (5)
"channel" : {
"$ref" : "#/channels/Movement"
},
"messages" : [ {
"$ref" : "#/channels/Movement/messages/getSpeedOutput" (2)
} ]
}
},
"components" : {
"messages" : {
"getSpeedInput" : { (1) (4)
"name" : "getSpeedInput", (1)
"title" : "getSpeed input",
"summary" : "The input to the getSpeed operation",
"content-type" : "application/json",
"payload" : {
"$ref" : "#/components/schemas/getSpeedInput" (1)
}
},
"getSpeedOutput" : { (2) (4)
"name" : "getSpeedOutput", (2)
"title" : "getSpeed output",
"summary" : "Return value of the getSpeed operation",
"content-type" : "application/json",
"payload" : {
"$ref" : "#/components/schemas/getSpeedOutput" (2)
}
}
},
"schemas" : {
"getSpeedInput" : { (1) (3)
"type" : "string",
"description" : "The input to the getSpeed operation"
},
"getSpeedOutput" : { (2) (3)
"type" : "string",
"description" : "Return value of the getSpeed operation"
}
}
}
}
1 | the names of the input |
2 | the names of the output |
3 | the characteristics are generated |
4 | the meta information of input/output |
5 | 'send' (for output) action: map to a publish operation in AsyncAPI, 'receive' (for input) action: map to a subscribe operation |
Mapping of Aspect’s events
This section describes the mapping between SAMM Events and the AsyncAPI specification. If the Aspect also has a non-empty list of events defined, like the one in the following example:
:Movement a samm:Aspect ;
samm:preferredName "movement"@en ;
samm:description "Aspect for movement information"@en ;
samm:properties ( ) ;
samm:operations ( ) ;
samm:events ( :SpeedUpdateEvent ) .
:SpeedUpdateEvent a samm:Event ;
samm:preferredName "Speed Update"@en ;
samm:description "This is event for update speed property"@en ;
samm:parameters ( :updatedSpeed :updateAcceleration ) .
:updatedSpeed a samm:Property ;
samm:preferredName "updated speed"@en ;
samm:description "the updated speed value"@en ;
samm:characteristic samm-c:Text .
:updateAcceleration a samm:Property ;
samm:preferredName "update acceleration"@en ;
samm:description "the updated acceleration value"@en ;
samm:characteristic samm-c:Text .
events will be generated in some places of AsyncAPI specification: operations section, channel messages section and schemas section:
{
"channels" : {
"Movement" : {
"address" : "movement/0.0.1/Movement",
"description" : "This channel for updating Movement Aspect.",
"parameters" : {
"namespace" : "movement",
"version" : "0.0.1",
"aspect-name" : "Movement"
},
"massages" : {
"SpeedUpdateEvent" : {
"$ref" : "#/components/messages/SpeedUpdateEvent" (1)
},
}
}
},
"operations" : {
"SpeedUpdateEvent" : {
"action" : "receive", (5)
"channel" : {
"$ref" : "#/channels/Movement"
},
"messages" : [ {
"$ref" : "#/channels/Movement/messages/SpeedUpdateEvent" (1)
} ]
},
},
"components" : {
"messages" : {
"SpeedUpdateEvent" : { (1) (2)
"name" : "SpeedUpdateEvent", (1)
"title" : "Speed Update",
"summary" : "This is event for update speed property",
"content-type" : "application/json",
"payload" : {
"$ref" : "#/components/schemas/SpeedUpdateEvent" (1)
}
}
},
"schemas" : {
"SpeedUpdateEvent" : { (1)
"type" : "object",
"properties" : {
"updatedSpeed" : { (3) (4)
"title" : "updated speed",
"type" : "string",
"description" : "the updated speed value"
},
"updateAcceleration" : { (3) (4)
"title" : "updated acceleration",
"type" : "string",
"description" : "the updated acceleration value"
}
}
}
}
}
}
1 | the names of the event |
2 | the meta information of event |
3 | property of event |
4 | the characteristics are generated |
5 | for events available only 'receive' action: map to a subscribe operation |
Understanding the models directory structure
An Aspect Model file can contain an Aspect definition as well as other model elements that are defined in the same versioned namespace, as described in the Namespaces section of the specification. Additionally, it is possible to split one versioned namespace across multiple files, for example to define a Characteristic that is usable in multiple Aspects into its own file. In order for SAMM CLI to be able to resolve references to such externally defined model elements, the model files must be organized in a directory structure as follows:
namespace/version/name.ttl
where namespace corresponds to the hierarchical namespace that is part of the model element’s URN, e.g.
com.mycompany.myproduct
and version corresponds to the version of the namespace.
The resulting directory structure then looks like the following:
models root
└── com.mycompany.myproduct
├── 1.0.0
│ ├── MyAspect.ttl
│ ├── MyEntity.ttl
│ └── myProperty.ttl
└── 1.1.0
└── MyAspect.ttl
The name of the directory shown as models root above can be chosen freely.
The SAMM CLI will resolve the file path relative to the input file by following the folder structure described above.
Each of the files in the 1.0.0
directory should therefore have an empty prefix declaration such as @prefix : <urn:samm:com.mycompany.myproduct:1.0.0#>
.