|
3 | 3 | [[bamm-cli]]
|
4 | 4 | = BAMM CLI
|
5 | 5 |
|
6 |
| -The BAMM 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. |
| 6 | +The BAMM 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. |
7 | 7 |
|
8 | 8 | TIP: Download latest version: icon:download[] https://github.com/OpenManufacturingPlatform/sds-sdk/releases/download/v{sds-sdk-version}/bamm-cli-{sds-sdk-version}.jar[bamm-cli-{sds-sdk-version}.jar]
|
9 | 9 |
|
@@ -140,10 +140,386 @@ The full command would result in:
|
140 | 140 | java -jar bamm-cli-{sds-sdk-version}.jar aspect _AspectModel.ttl_ to openapi -b "https://www.example.org" -r "/resources/\{resourceId}" -p _fileLocation_
|
141 | 141 | ----
|
142 | 142 |
|
| 143 | +=== Mapping between the Aspect Models and the OpenAPI Specification |
| 144 | + |
| 145 | +In this section, a detailed description of the mapping between individual Aspect elements and the OpenAPI specification is given. |
| 146 | +To make it easier to follow, the mapping is explained based on a concrete example, divided into logically coherent blocks. |
| 147 | +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. |
| 148 | + |
| 149 | +==== Naming and versioning |
| 150 | + |
| 151 | +Please consider the following model fragment, with the attention focused on the numbered elements: |
| 152 | + |
| 153 | +---- |
| 154 | +@prefix bamm: <urn:bamm:io.openmanufacturing:meta-model:2.0.0#>. |
| 155 | +@prefix : <urn:bamm:test:2.0.0#>. <1> |
| 156 | +
|
| 157 | +:Test a bamm:Aspect; <3> |
| 158 | + bamm:preferredName "TestAspect"@en ; <2> |
| 159 | + bamm:preferredName "TestAspekt"@de . |
| 160 | +---- |
| 161 | + |
| 162 | +<1> prefix used to build the full URN of :Test Aspect |
| 163 | +<2> the preferred name of the Aspect in language of user's choice |
| 164 | +<3> the name of the Aspect |
| 165 | + |
| 166 | +For the generated OpenAPI Specification, the following mapping would apply: |
| 167 | + |
| 168 | +[source,JSON] |
| 169 | +---- |
| 170 | +{ |
| 171 | + "openapi" : "3.0.3", |
| 172 | + "info" : { |
| 173 | + "title" : "TestAspect", // <2> <3> |
| 174 | + "version" : "v2" // <1> |
| 175 | + } |
| 176 | +} |
| 177 | +---- |
| 178 | + |
| 179 | +<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 |
| 180 | +<2> if present, `bamm:preferredName` is used as the value for the `title` element of the specification |
| 181 | +<3> as `bamm:preferredName` is an optional element, in cases when it is missing the name of the Aspect is used instead |
| 182 | + |
| 183 | +The version information as described above is also used in the URL definitions of the `servers` block of the specification: |
| 184 | + |
| 185 | +[source,JSON] |
| 186 | +---- |
| 187 | +{ |
| 188 | + "servers" : [ { |
| 189 | + "url" : "http://mysite/api/v2", // <1> |
| 190 | + "variables" : { |
| 191 | + "api-version" : { |
| 192 | + "default" : "v2" // <1> |
| 193 | + } |
| 194 | + } |
| 195 | + } ] |
| 196 | +} |
| 197 | +---- |
| 198 | + |
| 199 | +The name of the Aspect is used to generate several important OpenAPI artifacts, like the path definitions for the API: |
| 200 | + |
| 201 | +[source,JSON] |
| 202 | +---- |
| 203 | +{ |
| 204 | + "paths" : { |
| 205 | + "/{tenant-id}/test" : { // <3> |
| 206 | + "get" : { |
| 207 | + "tags" : [ "Test" ], // <3> |
| 208 | + "operationId" : "getTest" // <3> |
| 209 | + } |
| 210 | + } |
| 211 | + } |
| 212 | +} |
| 213 | +---- |
| 214 | + |
| 215 | +and the definitions for request bodies and responses in the corresponding blocks (`requestBodies` and `responses`) of the OpenAPI specification (example omitted for simplicity). |
| 216 | + |
| 217 | +==== Mapping of Aspect and its properties |
| 218 | + |
| 219 | +For each Aspect in the model, an entry in the `components/schemas` part of the OpenAPI specification is generated. |
| 220 | +For an example Aspect from the following fragment: |
| 221 | + |
| 222 | +---- |
| 223 | +:Test a bamm:Aspect; <1> |
| 224 | + bamm:properties ( |
| 225 | + :prop1 <2> |
| 226 | + [ bamm:property :prop2; bamm:payloadName "givenName"; ] <3> |
| 227 | + [ bamm:property :prop3; bamm:optional true; ] ). <4> |
| 228 | +
|
| 229 | +:prop1 a bamm:Property; |
| 230 | + bamm:description "Description of Property1"@en; <5> |
| 231 | + bamm:characteristic :Enum. <6> |
| 232 | +---- |
| 233 | + |
| 234 | +an entry like the one given in the following JSON will be generated: |
| 235 | + |
| 236 | +[source,JSON] |
| 237 | +---- |
| 238 | +"Test" : { // <1> |
| 239 | + "type" : "object", |
| 240 | + "properties" : { |
| 241 | + "prop1" : { // <2> |
| 242 | + "description" : "Description of Property1", // <5> |
| 243 | + "$ref" : "#/components/schemas/urn_bamm_test_2.0.0_Enum" // <6> |
| 244 | + }, |
| 245 | + "givenName" : { // <3> |
| 246 | + "$ref" : "#/components/schemas/urn_bamm_test_2.0.0_EntityChar" |
| 247 | + }, |
| 248 | + "prop3" : { // <4> |
| 249 | + "$ref" : "#/components/schemas/urn_bamm_test_2.0.0_StringCharacteristic" |
| 250 | + } |
| 251 | + }, |
| 252 | + "required" : [ "prop1", "givenName" ] // <2> <3> |
| 253 | +} |
| 254 | +---- |
| 255 | + |
| 256 | +<1> the name of the Aspect is used to name the schema object for the aspect |
| 257 | +<2> with plain property references, the name of the property is used to name the property definition |
| 258 | +<3> in cases where a payload name is defined on a specific property, it is used in preference to the plain property name |
| 259 | +<4> if the property use is also defined as optional, the property will not be included in the list of the required properties |
| 260 | +<5> the values of `bamm:description` elements in property definitions are included in the generated JSON |
| 261 | +<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 |
| 262 | + |
| 263 | +==== Mapping of Aspect's operations |
| 264 | + |
| 265 | +If the Aspect also has a non-empty list of operations defined, like the one in the following example: |
| 266 | + |
| 267 | +---- |
| 268 | +:AspectWithOperation a bamm:Aspect ; |
| 269 | + bamm:properties ( ) ; |
| 270 | + bamm:operations ( :testOperation ) . |
| 271 | +
|
| 272 | +:testOperation a bamm:Operation ; |
| 273 | + bamm:input ( :input ) ; <1> |
| 274 | + bamm:output :output . <2> |
| 275 | +
|
| 276 | +:output a bamm:Property ; |
| 277 | + bamm:characteristic bamm-c:Text . <3> |
| 278 | +
|
| 279 | +:input a bamm:Property ; |
| 280 | + bamm:characteristic bamm-c:Text . <4> |
| 281 | +---- |
| 282 | + |
| 283 | +then additional entries are added to the generated OpenAPI specification. |
| 284 | +First, there is an additional entry in the `paths` section of the specification: `/{tenant-id}/aspect-with-operation/*operations*`. |
| 285 | +The available operations are then added to the `components/schemas` part: |
| 286 | + |
| 287 | +[source,JSON] |
| 288 | +---- |
| 289 | +{ |
| 290 | + "Operation" : { |
| 291 | + "allOf" : [ { |
| 292 | + "$ref" : "#/components/schemas/JsonRpc" |
| 293 | + }, { |
| 294 | + "properties" : { |
| 295 | + "params" : { |
| 296 | + "type" : "object", |
| 297 | + "required" : [ "input" ], // <1> |
| 298 | + "properties" : { |
| 299 | + "input" : { // <1> |
| 300 | + "$ref" : "#/components/schemas/urn_bamm_io.openmanufacturing_characteristic_2.0.0_Text" // <3> |
| 301 | + } |
| 302 | + } |
| 303 | + }, |
| 304 | + "method" : { |
| 305 | + "type" : "string", |
| 306 | + "description" : "The method name", |
| 307 | + "example" : "testOperation" |
| 308 | + } |
| 309 | + } |
| 310 | + } ] |
| 311 | + }, |
| 312 | + "OperationResponse" : { |
| 313 | + "allOf" : [ { |
| 314 | + "$ref" : "#/components/schemas/JsonRpc" |
| 315 | + }, { |
| 316 | + "properties" : { |
| 317 | + "result" : { |
| 318 | + "type" : "object", |
| 319 | + "required" : [ "output" ], // <2> |
| 320 | + "properties" : { |
| 321 | + "output" : { // <2> |
| 322 | + "$ref" : "#/components/schemas/urn_bamm_io.openmanufacturing_characteristic_2.0.0_Text" // <4> |
| 323 | + } |
| 324 | + } |
| 325 | + } |
| 326 | + } |
| 327 | + } ] |
| 328 | + } |
| 329 | +} |
| 330 | +---- |
| 331 | + |
| 332 | +<1> the names of the input |
| 333 | +<2> and output parameters are reflected in the properties generated for the request/response objects |
| 334 | +<3> the characteristics are generated |
| 335 | +<4> and referenced as described in the point 6 of the section "Mapping of Aspect and its properties" |
| 336 | + |
| 337 | +As usual, corresponding entries referencing the definitions above are added to the `requestBodies` and `responses` sections (examples omitted for simplicity). |
| 338 | +For technical reasons, there may be a slight variation in the generated JSON depending on whether the aspect has one or more operations defined. |
| 339 | + |
| 340 | +==== Mapping of Collections |
| 341 | + |
| 342 | +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. |
| 343 | +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: |
| 344 | + |
| 345 | +* a general Collection - cursor and/or offset based paging |
| 346 | +* TimeSeries - cursor, offset and/or time based paging |
| 347 | + |
| 348 | +For all these paging mechanisms, an additional entry with the name `PagingSchema` is generated in the `components/schemas` part of the specification, |
| 349 | +which is then used as the main response schema for the Aspect. Basically, instead of a single Aspect, a collection of Aspects is returned, |
| 350 | +together with optional total number of Aspects available in the collection: |
| 351 | + |
| 352 | +[source,JSON] |
| 353 | +---- |
| 354 | +"PagingSchema" : { |
| 355 | + "type" : "object", |
| 356 | + "properties" : { |
| 357 | + "items" : { |
| 358 | + "type" : "array", |
| 359 | + "items" : { |
| 360 | + "$ref" : "#/components/schemas/Test" |
| 361 | + } |
| 362 | + }, |
| 363 | + "totalItems" : { |
| 364 | + "type" : "number" |
| 365 | + } |
| 366 | + } |
| 367 | +} |
| 368 | +---- |
| 369 | + |
| 370 | +Depending on the concrete paging model selected, there can be additional properties in the `PagingSchema` object. |
| 371 | +For cursor based paging, the `cursor` object denotes the position of the returned Aspects in relation to some other |
| 372 | +uniquely identifiable Aspect (`before` or `after` it): |
| 373 | + |
| 374 | +[source,JSON] |
| 375 | +---- |
| 376 | +"cursor" : { |
| 377 | + "type" : "object", |
| 378 | + "properties" : { |
| 379 | + "before" : { |
| 380 | + "type" : "string", |
| 381 | + "format" : "uuid" |
| 382 | + }, |
| 383 | + "after" : { |
| 384 | + "type" : "string", |
| 385 | + "format" : "uuid" |
| 386 | + } |
| 387 | + } |
| 388 | +}, |
| 389 | +---- |
| 390 | + |
| 391 | +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): |
| 392 | + |
| 393 | +[source,JSON] |
| 394 | +---- |
| 395 | +"totalPages" : { |
| 396 | + "type" : "number" |
| 397 | +}, |
| 398 | +"pageSize" : { |
| 399 | + "type" : "number" |
| 400 | +}, |
| 401 | +"currentPage" : { |
| 402 | + "type" : "number" |
| 403 | +} |
| 404 | +---- |
| 405 | + |
| 406 | +In addition to the `PagingSchema` object, also several new parameters are added to the request parameters section of the generated document, |
| 407 | +with the help of which the size and/or the relative position of the returned data can be controlled. |
| 408 | +All paging mechanisms have the following parameters in common, the meaning of which can be discerned from their descriptions: |
| 409 | + |
| 410 | +[source,JSON] |
| 411 | +---- |
| 412 | +{ |
| 413 | + "name" : "count", |
| 414 | + "in" : "query", |
| 415 | + "description" : "Number of items to return per call.", |
| 416 | + "required" : false, |
| 417 | + "schema" : { |
| 418 | + "type" : "number" |
| 419 | + } |
| 420 | +}, |
| 421 | +{ |
| 422 | + "name" : "totalItemCount", |
| 423 | + "in" : "query", |
| 424 | + "description" : "Flag that indicates that the total counts should be returned.", |
| 425 | + "required" : false, |
| 426 | + "schema" : { |
| 427 | + "type" : "boolean" |
| 428 | + } |
| 429 | +} |
| 430 | +---- |
| 431 | + |
| 432 | +Depending on the exact paging model selected, additional paging specific parameters are available. |
| 433 | +For offset based paging: |
| 434 | +[source,JSON] |
| 435 | +---- |
| 436 | +"name" : "start", |
| 437 | +"in" : "query", |
| 438 | +"description" : "Starting index which is starting by 0", |
| 439 | +"required" : false, |
| 440 | +"schema" : { |
| 441 | + "type" : "number" |
| 442 | +} |
| 443 | +---- |
| 444 | + |
| 445 | +For cursor based paging: |
| 446 | +[source,JSON] |
| 447 | +---- |
| 448 | +{ |
| 449 | + "name" : "previous", |
| 450 | + "in" : "query", |
| 451 | + "description" : "URL to request the previous items. An empty value indicates there are no previous items.", |
| 452 | + "required" : false, |
| 453 | + "schema" : { |
| 454 | + "type" : "string", |
| 455 | + "format" : "uri" |
| 456 | + } |
| 457 | +},{ |
| 458 | + "name" : "next", |
| 459 | + "in" : "query", |
| 460 | + "description" : "URL to request the next items. An empty value indicates there are no other items.", |
| 461 | + "required" : false, |
| 462 | + "schema" : { |
| 463 | + "type" : "string", |
| 464 | + "format" : "uri" |
| 465 | + } |
| 466 | +}, { |
| 467 | + "name" : "before", |
| 468 | + "in" : "query", |
| 469 | + "description" : "The cursor that points to the start of the page of items that has been returned.", |
| 470 | + "required" : false, |
| 471 | + "schema" : { |
| 472 | + "type" : "string", |
| 473 | + "format" : "uuid" |
| 474 | + } |
| 475 | +}, { |
| 476 | + "name" : "after", |
| 477 | + "in" : "query", |
| 478 | + "description" : "The cursor that points to the end of items that has been returned.", |
| 479 | + "required" : false, |
| 480 | + "schema" : { |
| 481 | + "type" : "string", |
| 482 | + "format" : "uuid" |
| 483 | + } |
| 484 | +} |
| 485 | +---- |
| 486 | + |
| 487 | +And finally for the time based paging: |
| 488 | +[source,JSON] |
| 489 | +---- |
| 490 | +{ |
| 491 | + "name" : "since", |
| 492 | + "in" : "query", |
| 493 | + "description" : "A timestamp that points to the start of the time-based data.", |
| 494 | + "required" : false, |
| 495 | + "schema" : { |
| 496 | + "type" : "string", |
| 497 | + "format" : "date-time" |
| 498 | + } |
| 499 | +}, { |
| 500 | + "name" : "until", |
| 501 | + "in" : "query", |
| 502 | + "description" : "A timestamp that points to the end of the time-based data.", |
| 503 | + "required" : false, |
| 504 | + "schema" : { |
| 505 | + "type" : "string", |
| 506 | + "format" : "date-time" |
| 507 | + } |
| 508 | +}, { |
| 509 | + "name" : "limit", |
| 510 | + "in" : "query", |
| 511 | + "description" : "Number of items to return per call.", |
| 512 | + "required" : false, |
| 513 | + "schema" : { |
| 514 | + "type" : "number" |
| 515 | + } |
| 516 | +} |
| 517 | +---- |
| 518 | + |
143 | 519 | [[models-directory-structure]]
|
144 | 520 | == Understanding the models directory structure
|
145 | 521 |
|
146 |
| -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 xref:bamm-specification:ROOT:namespaces.adoc[Namespaces section of the |
| 522 | +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 xref:bamm-specification:ROOT:namespaces.adoc[Namespaces section of the |
147 | 523 | specification].
|
148 | 524 | 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.
|
149 | 525 | In order for BAMM 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:
|
|
0 commit comments