Skip to content

Commit de08fb1

Browse files
authored
Merge pull request #269 from bci-oss/feature/OMP-SDK-248-document-OpenAPI-JSON-schema-mapping
Describe the mapping between Aspects and the OpenAPI specification.
2 parents 058a733 + 023ebb7 commit de08fb1

File tree

1 file changed

+378
-2
lines changed
  • documentation/developer-guide/modules/tooling-guide/pages

1 file changed

+378
-2
lines changed

documentation/developer-guide/modules/tooling-guide/pages/bamm-cli.adoc

Lines changed: 378 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[[bamm-cli]]
44
= BAMM CLI
55

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

88
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]
99

@@ -140,10 +140,386 @@ The full command would result in:
140140
java -jar bamm-cli-{sds-sdk-version}.jar aspect _AspectModel.ttl_ to openapi -b "https://www.example.org" -r "/resources/\{resourceId}" -p _fileLocation_
141141
----
142142

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+
143519
[[models-directory-structure]]
144520
== Understanding the models directory structure
145521

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
147523
specification].
148524
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.
149525
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

Comments
 (0)