Skip to content

Commit 312778c

Browse files
authored
Merge pull request #242 from bci-oss/bugfix/#231_fix_missing_description
[#231] Fix missing descriptions in OpenAPI and json schema during generation
2 parents 5ba4bd2 + 0c9a874 commit 312778c

File tree

30 files changed

+509
-83
lines changed

30 files changed

+509
-83
lines changed

core/sds-aspect-meta-model-java/src/test/java/io/openmanufacturing/sds/metamodel/loader/AspectMetaModelInstantiatorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public void testCollectionWithAbstractEntityInstantiationExpectSuccess( final Kn
199199

200200
final AbstractEntity abstractEntity = (AbstractEntity) aspect.getProperties().get( 0 ).getCharacteristic().get().getDataType().get();
201201
assertThat( abstractEntity.getExtends() ).isEmpty();
202-
assertBaseAttributes( abstractEntity, expectedAspectModelUrn, "AbstractTestEntity", "AbstractTestEntity", null );
202+
assertBaseAttributes( abstractEntity, expectedAspectModelUrn, "AbstractTestEntity", "AbstractTestEntity", "This is an abstract test entity" );
203203
final List<ComplexType> extendingElements = abstractEntity.getExtendingElements();
204204
assertThat( extendingElements ).hasSize( 1 );
205205
}

core/sds-aspect-model-document-generators/src/main/java/io/openmanufacturing/sds/aspectmodel/generator/jsonschema/AspectModelJsonSchemaGenerator.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,23 @@
1313

1414
package io.openmanufacturing.sds.aspectmodel.generator.jsonschema;
1515

16-
import java.util.function.Function;
17-
1816
import com.fasterxml.jackson.databind.JsonNode;
19-
2017
import io.openmanufacturing.sds.metamodel.Aspect;
18+
import java.util.Locale;
19+
import java.util.function.BiFunction;
2120

2221
/**
2322
* Generator that generates a JSON Schema for payloads corresponding to a given Aspect model.
2423
*/
25-
public class AspectModelJsonSchemaGenerator implements Function<Aspect, JsonNode> {
26-
24+
public class AspectModelJsonSchemaGenerator implements BiFunction<Aspect, Locale, JsonNode> {
2725
@Override
28-
public JsonNode apply( final Aspect aspect ) {
29-
final AspectModelJsonSchemaVisitor visitor = new AspectModelJsonSchemaVisitor(true);
26+
public JsonNode apply( final Aspect aspect, final Locale locale ) {
27+
final AspectModelJsonSchemaVisitor visitor = new AspectModelJsonSchemaVisitor(true, locale);
3028
return visitor.visitAspect( aspect, null );
3129
}
3230

33-
public JsonNode applyForOpenApi( final Aspect aspect ) {
34-
final AspectModelJsonSchemaVisitor visitor = new AspectModelJsonSchemaVisitor(false);
31+
public JsonNode applyForOpenApi( final Aspect aspect, final Locale locale ) {
32+
final AspectModelJsonSchemaVisitor visitor = new AspectModelJsonSchemaVisitor(false, locale);
3533
return visitor.visitAspectForOpenApi( aspect );
3634
}
3735
}

core/sds-aspect-model-document-generators/src/main/java/io/openmanufacturing/sds/aspectmodel/generator/jsonschema/AspectModelJsonSchemaVisitor.java

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313

1414
package io.openmanufacturing.sds.aspectmodel.generator.jsonschema;
1515

16+
import com.google.common.base.Strings;
17+
import io.openmanufacturing.sds.metamodel.IsDescribed;
1618
import java.math.BigDecimal;
1719
import java.util.HashMap;
1820
import java.util.LinkedList;
1921
import java.util.List;
22+
import java.util.Locale;
2023
import java.util.Map;
2124
import java.util.Optional;
2225

@@ -138,6 +141,7 @@ ImmutableMap.<Resource, Map<String, JsonNode>> builder()
138141
private final Map<Resource, Map<String, JsonNode>> typeData;
139142

140143
static final String JSON_SCHEMA_VERSION = "http://json-schema.org/draft-04/schema";
144+
private final Locale locale;
141145

142146
private enum JsonType {
143147
NUMBER,
@@ -188,12 +192,18 @@ private JsonNode toJsonNode() {
188192
private final ObjectNode rootNode = factory.objectNode();
189193
private final Map<Base, JsonNode> hasVisited = new HashMap<>();
190194

191-
public AspectModelJsonSchemaVisitor( final boolean useExtendedTypes ) {
195+
public AspectModelJsonSchemaVisitor( final boolean useExtendedTypes, final Locale locale ) {
192196
if ( useExtendedTypes ) {
193197
typeData = extendedTypeData;
194198
} else {
195199
typeData = openApiTypeData;
196200
}
201+
202+
this.locale = locale;
203+
}
204+
205+
public AspectModelJsonSchemaVisitor( final boolean useExtendedTypes ) {
206+
this( useExtendedTypes, Locale.ENGLISH );
197207
}
198208

199209
public ObjectNode getRootNode() {
@@ -206,12 +216,14 @@ public JsonNode visitBase( final Base base, final ObjectNode context ) {
206216
}
207217

208218
public JsonNode visitAspectForOpenApi( final Aspect aspect ) {
219+
addDescription( rootNode, aspect, locale );
209220
return visitHasProperties( aspect, rootNode );
210221
}
211222

212223
@Override
213224
public JsonNode visitAspect( final Aspect aspect, final ObjectNode context ) {
214225
rootNode.put( "$schema", JSON_SCHEMA_VERSION );
226+
addDescription( rootNode, aspect, locale );
215227
return visitHasProperties( aspect, rootNode );
216228
}
217229

@@ -266,6 +278,7 @@ private Characteristic determineCharacteristic( final Property property ) {
266278
@SuppressWarnings( "squid:S2250" )
267279
//Amount of elements in list is in regard to amount of properties in aspect model. Even in bigger aspects this should not lead to performance issues
268280
public JsonNode visitProperty( final Property property, final ObjectNode context ) {
281+
final ObjectNode propertyNode = addDescription( factory.objectNode(), property, locale );
269282
final Characteristic characteristic = determineCharacteristic( property );
270283
final String referenceNodeName;
271284
if ( characteristic instanceof SingleEntity ) {
@@ -278,12 +291,12 @@ public JsonNode visitProperty( final Property property, final ObjectNode context
278291
}
279292

280293
if ( processedProperties.contains( property ) ) {
281-
return factory.objectNode().put( "$ref", "#/components/schemas/" + referenceNodeName );
294+
return propertyNode.put( "$ref", "#/components/schemas/" + referenceNodeName );
282295
}
283296
processedProperties.add( property );
284297
final JsonNode characteristicSchema = characteristic.accept( this, context );
285298
setNodeInRootSchema( characteristicSchema, referenceNodeName );
286-
return factory.objectNode().put( "$ref", "#/components/schemas/" + referenceNodeName );
299+
return propertyNode.put( "$ref", "#/components/schemas/" + referenceNodeName );
287300
}
288301

289302
private AspectModelJsonSchemaVisitor.JsonType getSchemaTypeForAspectType( final Resource type ) {
@@ -314,6 +327,7 @@ public JsonNode visitSortedSet( final SortedSet sortedSet, final ObjectNode cont
314327
@Override
315328
public JsonNode visitCollection( final Collection collection, final ObjectNode context ) {
316329
final ObjectNode collectionNode = factory.objectNode();
330+
addDescription( collectionNode, collection, locale );
317331
collectionNode.put( "type", "array" );
318332
final Optional<Characteristic> characteristic = collection.getElementCharacteristic();
319333
if ( characteristic.isPresent() ) {
@@ -338,16 +352,19 @@ public JsonNode visitCollection( final Collection collection, final ObjectNode c
338352
@Override
339353
public JsonNode visitTrait( final Trait trait, final ObjectNode context ) {
340354
final ObjectNode propertyNode = (ObjectNode) trait.getBaseCharacteristic().accept( this, context );
355+
addDescription( propertyNode, trait, locale );
341356
return Stream.ofAll( trait.getConstraints() ).foldLeft( propertyNode, ( node, constraint ) -> ((ObjectNode) (constraint.accept( this, node ))) );
342357
}
343358

344359
@Override
345360
public JsonNode visitConstraint( final Constraint constraint, final ObjectNode context ) {
361+
addDescription( context, constraint, locale );
346362
return context;
347363
}
348364

349365
@Override
350366
public JsonNode visitLengthConstraint( final LengthConstraint lengthConstraint, final ObjectNode context ) {
367+
addDescription( context, lengthConstraint, locale );
351368
final String itemsOrLength = "array".equals( context.get( "type" ).asText() ) ? "Items" : "Length";
352369
lengthConstraint.getMaxValue().ifPresent( maxValue -> context.put( "max" + itemsOrLength, maxValue ) );
353370
lengthConstraint.getMinValue().ifPresent( minValue -> context.put( "min" + itemsOrLength, minValue ) );
@@ -365,12 +382,14 @@ private Try<JsonNode> getNumberNode( final Object value ) {
365382
@Override
366383
public JsonNode visitRegularExpressionConstraint( final RegularExpressionConstraint regularExpressionConstraint,
367384
final ObjectNode context ) {
385+
addDescription( context, regularExpressionConstraint, locale );
368386
context.set( "pattern", factory.textNode( regularExpressionConstraint.getValue() ) );
369387
return context;
370388
}
371389

372390
@Override
373391
public JsonNode visitRangeConstraint( final RangeConstraint rangeConstraint, final ObjectNode context ) {
392+
addDescription( context, rangeConstraint, locale );
374393
rangeConstraint.getMaxValue().map( maxValue -> maxValue.accept( this, context ) ).ifPresent( value -> {
375394
if ( value instanceof NumericNode ) {
376395
context.set( "maximum", value );
@@ -401,7 +420,7 @@ public JsonNode visitEither( final Either either, final ObjectNode context ) {
401420
properties.set( "left", either.getLeft().accept( this, properties ) );
402421
properties.set( "right", either.getRight().accept( this, properties ) );
403422

404-
return factory.objectNode()
423+
return addDescription( factory.objectNode(), either , locale)
405424
.put( "additionalProperties", false )
406425
.<ObjectNode> set( "properties", properties )
407426
.set( "oneOf",
@@ -411,9 +430,12 @@ public JsonNode visitEither( final Either either, final ObjectNode context ) {
411430
}
412431

413432
@Override
414-
public JsonNode visitCharacteristic( final Characteristic characteristic, final ObjectNode context ) {
415-
return characteristic.getDataType().map( type -> type.accept( this, context ) ).orElseThrow( () ->
416-
new DocumentGenerationException( "Characteristic " + characteristic + " is missing a dataType" ) );
433+
public JsonNode visitCharacteristic( final Characteristic characteristic,
434+
final ObjectNode context ) {
435+
final JsonNode dataTypeNode = characteristic.getDataType().map( type -> type.accept( this, context ) )
436+
.orElseThrow( () -> new DocumentGenerationException( "Characteristic " + characteristic + " is missing a dataType" ) );
437+
438+
return addDescription( ( ObjectNode ) dataTypeNode, characteristic, locale );
417439
}
418440

419441
private void setNodeInRootSchema( final JsonNode node, final String name ) {
@@ -435,6 +457,7 @@ public JsonNode visitComplexType( final ComplexType complexType, final ObjectNod
435457
final ObjectNode complexTypeNode = factory.objectNode();
436458
hasVisited.put( complexType, complexTypeNode );
437459

460+
addDescription( complexTypeNode, complexType, locale );
438461
// visitHasProperties needs to be called before accept() on the supertype:
439462
// This Entity's Properties could be extending an AbstractProperty on the supertype,
440463
// in order to know the corresponding Characteristic this must be visited first
@@ -526,6 +549,7 @@ private JsonNode createEnumNodeWithScalarValues( final Enumeration enumeration,
526549
final ObjectNode enumNode = type.getUrn().equals( RDF.langString.getURI() ) ?
527550
factory.objectNode().put( "type", "object" ) :
528551
(ObjectNode) type.accept( this, context );
552+
addDescription( enumNode, enumeration, locale );
529553
enumeration.getValues().stream()
530554
.map( value -> value.accept( this, enumNode ) )
531555
.forEach( valuesNode::add );
@@ -535,6 +559,7 @@ private JsonNode createEnumNodeWithScalarValues( final Enumeration enumeration,
535559

536560
private JsonNode createEnumNodeWithComplexValues( final Enumeration enumeration, final BAMM bamm ) {
537561
final ObjectNode enumNode = factory.objectNode();
562+
addDescription( enumNode, enumeration, locale );
538563
enumNode.put( "type", "object" );
539564
final ArrayNode enumValueReferences = factory.arrayNode();
540565
enumeration.getValues().stream()
@@ -559,6 +584,7 @@ private JsonNode createNodeForEnumEntityInstance( final EntityInstance entityIns
559584
final ObjectNode enumEntityInstanceNode = factory.objectNode();
560585
final ArrayNode required = factory.arrayNode();
561586
final ObjectNode properties = factory.objectNode();
587+
addDescription(enumEntityInstanceNode, entityInstance, locale);
562588
enumEntityInstanceNode.put( "type", "object" );
563589

564590
final Entity entity = entityInstance.getEntityType();
@@ -592,6 +618,7 @@ private JsonNode createNodeForEnumEntityPropertyInstance( final Property propert
592618
}
593619
if ( characteristic.is( Collection.class ) ) {
594620
final ObjectNode propertyInstanceNode = factory.objectNode();
621+
addDescription( propertyInstanceNode, property, locale );
595622
propertyInstanceNode.put( "type", "array" );
596623
final ObjectNode arrayPropertyInstanceNode = factory.objectNode();
597624
arrayPropertyInstanceNode.set( "type", schemaType.toJsonNode() );
@@ -603,8 +630,19 @@ private JsonNode createNodeForEnumEntityPropertyInstance( final Property propert
603630
return propertyInstanceNode;
604631
}
605632
final ObjectNode propertyInstanceNode = factory.objectNode();
633+
addDescription( propertyInstanceNode, property, locale );
606634
propertyInstanceNode.set( "type", schemaType.toJsonNode() );
607635
propertyInstanceNode.set( "enum", factory.arrayNode().add( valueForProperty.accept( this, propertyInstanceNode ) ) );
608636
return propertyInstanceNode;
609637
}
638+
639+
private static ObjectNode addDescription( final ObjectNode node,
640+
final IsDescribed describedElement, final Locale locale ) {
641+
final String description = describedElement.getDescription( locale );
642+
643+
if ( !Strings.isNullOrEmpty( description ) ) {
644+
node.put( "description", description );
645+
}
646+
return node;
647+
}
610648
}

0 commit comments

Comments
 (0)