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 samm-cli easier to call, you can add an alias to the configuration of the shell of your choice. For example, for Bash, you could add the following line to .bashrc:

alias samm='java -jar /location/to/samm-cli-2.9.8.jar'
  1. 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 further samm commands.

    • Input: Make sure to read the below documentation and provide model files in the correct directory structure.

  2. 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 <model> in the command descriptions below, you can provide either one of:

  • a relative file name, such as AspectModel.ttl or ./somewhere/AspectModel.ttl (on Linux and MacOS) or .\somewhere\AspectModel.ttl (on Windows),

  • an absolut file name, such as /home/me/aspect-models/org.eclipse.esmf.example/1.0.0/AspectModel.ttl (on Linux and MacOS) or C:\Users\me\aspect-models\org.eclipse.esmf.example\1.0.0\AspectModel.ttl (on Windows),

  • a URL pointing to a .ttl file in a publically accessible GitHub repository, such as https://github.com/eclipse-tractusx/sldt-semantic-models/blob/main/io.catenax.battery.battery_pass/6.0.0/BatteryPass.ttl or https://github.com/eclipse-esmf/esmf-aspect-model-editor/blob/main/core/apps/ame/src/assets/aspect-models/org.eclipse.examples/1.0.0/Movement.ttl,

  • an Aspect Model URN, such as urn:samm:org.eclipse.esmf.example:1.0.0#Aspect, however note that this also requires setting a models source, see configuration of model resolution for more information.

Command Description/Options Examples

help

Get overview of all commands

samm help

help <commands…​>

Get help for a list of subcommands

samm help aspect

samm help aspect to svg

samm help aspect validate

aspect <model> validate

Validate Aspect Model

samm aspect AspectModel.ttl validate

--custom-resolver : use an external resolver for the resolution of the model elements

samm aspect AspectModel.ttl validate --custom-resolver myresolver.sh

aspect <model> prettyprint

Pretty-print Aspect Model

samm aspect AspectModel.ttl prettyprint

--output, -o : the output will be saved to the given file

samm aspect AspectModel.ttl prettyprint -o c:\Results\PrettyPrinted.ttl

--overwrite, -w : Overwrite the input file

samm aspect AspectModel.ttl prettyprint -w

aspect <model> to html

Generate HTML documentation for an Aspect Model

samm aspect AspectModel.ttl to html

--output, -o : the output will be saved to the given file

samm aspect AspectModel.ttl to html -o c:\Model.html

--css, -c : CSS file with custom styles to be included in the generated HTML documentation

samm aspect AspectModel.ttl to html -c c:\styles.css

--language, -l : The language from the model for which the HTML should be generated (default: en)

samm aspect AspectModel.ttl to html -l de

--custom-resolver : use an external resolver for the resolution of the model elements

samm aspect AspectModel.ttl to html --custom-resolver myresolver.bat

aspect <model> to png

Generate PNG diagram for Aspect Model

samm aspect AspectModel.ttl to png

--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

samm aspect AspectModel.ttl to png --custom-resolver resolver.jar

aspect <model> to svg

Generate SVG diagram for Aspect Model

samm aspect AspectModel.ttl to svg

--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

samm aspect AspectModel.ttl to svg --custom-resolver "java -jar resolver.jar"

aspect <model> to java

Generate Java classes from an Aspect Model

samm aspect AspectModel.ttl to java

--output-directory, -d : output directory to write files to (default: current directory)

--package-name, -pn : package to use for generated Java classes

samm aspect AspectModel.ttl to java -pn org.company.product

--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

samm aspect AspectModel.ttl to java -namePrefix "Prefix"

--name-postfix, -namePostfix : name postfix for generated Aspect, Entity Java classes

samm aspect AspectModel.ttl to java -namePostfix "Postfix"

aspect <model> to openapi

Generate OpenAPI specification for an Aspect Model

samm aspect AspectModel.ttl to openapi -j

--output, -o : output file path (default: stdout)

--api-base-url, -b : the base url for the Aspect API used in the OpenAPI specification

samm aspect AspectModel.ttl to openapi -j -b http://example.org

--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, 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)

samm aspect AspectModel.ttl to openapi -l de

--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

aspect <model> to asyncapi

Generate AsyncAPI specification for an Aspect Model

samm aspect AspectModel.ttl to asyncapi

--output, -o : output file path (default: stdout)

--channel-address, -ca : Sets the channel address (i.e., for MQTT, the topic’s name). OpenAPI specification

samm aspect AspectModel.ttl to asyncapi -ca 123-456/789-012/namespace/1.0.0/Aspect

--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)

samm aspect AspectModel.ttl to asyncapi -l de

--separate-files, -sf : Create separate files for each schema

--custom-resolver : use an external resolver for the resolution of the model elements

aspect <model> to json

Generate example JSON payload data for an Aspect Model

samm aspect AspectModel.ttl to json

--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 @type attribute for inherited Entities

aspect <model> to jsonld

Generate JSON-LD representation of an Aspect Model

samm aspect AspectModel.ttl to jsonld

--output, -o : output file path (default: stdout)

--custom-resolver : use an external resolver for the resolution of the model elements

aspect <model> to schema

Generate JSON schema for an Aspect Model

samm aspect AspectModel.ttl to schema

--output, -o : output file path (default: stdout)

--language, -l : The language from the model for which a JSON schema should be generated (default: en)

samm aspect AspectModel.ttl to schema -l de

--custom-resolver : use an external resolver for the resolution of the model elements

aspect <model> to sql

Generate SQL script that sets up a table for data for this Aspect

samm aspect AspectModel.ttl to sql

--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: databricks)

--mapping-strategy, -s : The mapping strategy to use (default: denormalized)

--include-table-comment, -tc : Include table comment in the generated SQL script (default: true)

--include-column-comments, -cc : Include column comments in the generated SQL script (default: true)

--table-command-prefix, -tcp : The prefix to use for Databricks table creation commands (default: CREATE TABLE IF NOT EXISTS)

--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 column_name DATATYPE [NOT NULL] [COMMENT 'custom']. This parameter can be repeated for multiple columns.

samm aspect AspectModel.ttl to sql --custom-column "column_name STRING NOT NULL COMMENT 'custom'"

aspect <model> to aas

Generate an Asset Administration Shell (AAS) submodel template from an Aspect Model

samm aspect AspectModel.ttl to aas

--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

aspect <model> edit move <element> [<namespace>]

Move a model element definition from its current place to another existing or new file in the same or another namespace.

samm aspect AspectModel.ttl edit move MyAspect otherFile.ttl or samm aspect AspectModel.ttl edit move MyAspect someFileInOtherNamespace.ttl urn:samm:org.eclipse.example.myns:1.0.0

--dry-run : Don’t write changes to the file system, but print a report of changes that would be performed.

--details : When used with --dry-run, include details about model content changes in the report .

--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 --force is used.

aspect <model> edit newversion [--major | --minor | --micro]

Create a new version of an existing file or a complete namespace. model can be an Aspect Model file or a namespace URN. If model is a URN, at least one --models-root must also be specified.

samm aspect AspectModel.ttl edit newversion --major

--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 --dry-run, include details about model content changes in the report .

--force : When a new file is to be created but it already exists in the file system, the operation will be cancelled, unless --force is used.

aspect <model> usage

Shows where model elements are used in an Aspect. model can be an Aspect Model file or an element URN. If model is a URN, at least one --models-root must also be specified.

samm aspect AspectModelFile.ttl usage

aas <aas file> to aspect

Translate Asset Administration Shell (AAS) Submodel Templates to Aspect Models

samm aas AssetAdminShell.aasx to aspect

--output-directory, -d : output directory to write files to (default: current directory)

--submodel-template, -s : selected submodel template for generating; run samm aas <aas file> list to list them.

samm aas AssetAdminShell.aasx to aspect -s 1 -s 2

aas <aas file> list

Retrieve a list of submodel templates contained within the provided Asset Administration Shell (AAS) file.

samm aas AssetAdminShell.aasx list

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#>.