Skip to content

Commit 3de8910

Browse files
committed
[#249] Validate namespace of units not specified in the unit catalog
* Validation of aspect models containing units, which are not specified in the unit catalog and referred with bamm namespace must fail. Signed-off-by: Kartheeswaran Kalidass <[email protected]>
1 parent 6d48d90 commit 3de8910

File tree

7 files changed

+174
-10
lines changed

7 files changed

+174
-10
lines changed

core/sds-aspect-meta-model-java/src/main/java/io/openmanufacturing/sds/metamodel/loader/AspectModelLoader.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313

1414
package io.openmanufacturing.sds.metamodel.loader;
1515

16+
import java.util.ArrayList;
1617
import java.util.List;
1718
import java.util.Optional;
1819
import java.util.Set;
1920

2021
import org.apache.jena.rdf.model.Model;
22+
import org.apache.jena.rdf.model.Resource;
23+
import org.apache.jena.rdf.model.Statement;
2124
import org.apache.jena.rdf.model.StmtIterator;
2225
import org.apache.jena.vocabulary.RDF;
2326
import org.slf4j.Logger;
@@ -29,6 +32,7 @@
2932
import io.openmanufacturing.sds.aspectmodel.UnsupportedVersionException;
3033
import io.openmanufacturing.sds.aspectmodel.resolver.AspectModelResolver;
3134
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidModelException;
35+
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidNamespaceException;
3236
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidRootElementCountException;
3337
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidVersionException;
3438
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
@@ -73,9 +77,12 @@ private static Try<Aspect> load( final VersionedModel versionedModel ) {
7377
final BAMM bamm = new BAMM( metaModelVersion.get() );
7478
final StmtIterator iterator = model.listStatements( null, RDF.type, bamm.Aspect() );
7579
try {
80+
validateNamespaceOfCustomUnits( bamm, versionedModel.getRawModel() );
7681
final ModelElementFactory modelElementFactory = new ModelElementFactory( metaModelVersion.get(), model );
7782
final Aspect aspect = modelElementFactory.create( Aspect.class, iterator.nextStatement().getSubject() );
7883
return Try.success( aspect );
84+
} catch ( final InvalidNamespaceException exception ) {
85+
return Try.failure( exception );
7986
} catch ( final RuntimeException exception ) {
8087
return Try.failure( new InvalidModelException( "Could not load Aspect model, please make sure the model is valid", exception ) );
8188
}
@@ -114,6 +121,21 @@ public static Try<Aspect> fromVersionedModel( final VersionedModel versionedMode
114121
} );
115122
}
116123

124+
private static void validateNamespaceOfCustomUnits( final BAMM bamm, final Model rawModel ) {
125+
final List<String> customUnitsWithBammNamespace = new ArrayList<>();
126+
rawModel.listStatements( null, RDF.type, bamm.Unit() )
127+
.mapWith( Statement::getSubject )
128+
.filterKeep( subject -> subject.getNameSpace().equals( bamm.getNamespace() ) )
129+
.mapWith( Resource::getLocalName )
130+
.forEach( customUnitsWithBammNamespace::add );
131+
132+
if ( !customUnitsWithBammNamespace.isEmpty() ) {
133+
throw new InvalidNamespaceException(
134+
String.format( "Aspect model contains unit(s) %s not specified in the unit catalog but referred with bamm namespace",
135+
customUnitsWithBammNamespace ) );
136+
}
137+
}
138+
117139
private static List<Try<AspectModelUrn>> getUrns( final VersionedModel migratedModel, final KnownVersion version ) {
118140
final BAMM bamm = new BAMM( version );
119141
return migratedModel.getModel().listStatements( null, RDF.type, bamm.Aspect() ).mapWith( statement ->
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
package io.openmanufacturing.sds.aspectmodel.resolver.exceptions;
14+
15+
import java.io.Serial;
16+
17+
/**
18+
* An exception indicating that usage of the given namespace is invalid.
19+
*/
20+
public final class InvalidNamespaceException extends RuntimeException {
21+
22+
@Serial
23+
private static final long serialVersionUID = -1075433954587137319L;
24+
25+
/**
26+
* Creates an instance of the exception.
27+
* @param message The detailed message of the problem.
28+
* @param cause The cause of the problem.
29+
*/
30+
public InvalidNamespaceException( final String message, final Throwable cause ) {
31+
super( message, cause );
32+
}
33+
34+
/**
35+
* Creates an instance of the exception.
36+
* @param message The detailed message of the problem.
37+
*/
38+
public InvalidNamespaceException( final String message ) {
39+
super( message );
40+
}
41+
42+
}

core/sds-aspect-model-validator/src/main/java/io/openmanufacturing/sds/aspectmodel/validation/services/AspectModelValidator.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import io.openmanufacturing.sds.aspectmetamodel.KnownVersion;
4444
import io.openmanufacturing.sds.aspectmodel.UnsupportedVersionException;
4545
import io.openmanufacturing.sds.aspectmodel.resolver.ModelResolutionException;
46+
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidNamespaceException;
4647
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidRootElementCountException;
4748
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidVersionException;
4849
import io.openmanufacturing.sds.aspectmodel.resolver.services.SdsAspectMetaModelResourceResolver;
@@ -108,9 +109,9 @@ public AspectModelValidator() {
108109
/**
109110
* Validates an Aspect Model that is provided as a {@link Try} of a {@link VersionedModel} that can
110111
* contain either a syntactically valid (but semantically invalid) Aspect model, or a
111-
* {@link RiotException} if a parser error occured.
112+
* {@link RiotException} if a parser error occurred.
112113
*
113-
* @param versionedModel The Aspect Model or the corresonding parser error
114+
* @param versionedModel The Aspect Model or the corresponding parser error
114115
* @return Either a {@link ValidationReport.ValidReport} if the model is syntactically correct and conforms to the
115116
* Aspect Meta Model semantics or a {@link ValidationReport.InvalidReport} that provides a number of
116117
* {@link ValidationError}s that describe all validation violations.
@@ -132,13 +133,7 @@ public ValidationReport validate( final Try<VersionedModel> versionedModel ) {
132133
// The SHACL validation succeeded. But to catch false positives, also try to load the model
133134
final Try<Aspect> aspects = AspectModelLoader.fromVersionedModel( model );
134135
if ( aspects.isFailure() && !(aspects.getCause() instanceof InvalidRootElementCountException) ) {
135-
return new ValidationReportBuilder()
136-
.withValidationErrors( List.of( new ValidationError.Processing(
137-
"Validation succeeded, but an error was found while processing the model. "
138-
+ "This indicates an error in the model validation; please consider reporting this issue including the model "
139-
+ "at https://github.com/OpenManufacturingPlatform/sds-bamm-aspect-meta-model/issues -- "
140-
+ buildCauseMessage( aspects.getCause() ) ) ) )
141-
.buildInvalidReport();
136+
return getInvalidReport( aspects.getCause() );
142137
}
143138
return new ValidationReportBuilder().buildValidReport();
144139
}
@@ -153,7 +148,24 @@ public ValidationReport validate( final Try<VersionedModel> versionedModel ) {
153148
).get();
154149
}
155150

156-
private String buildCauseMessage( final Throwable throwable ) {
151+
private static ValidationReport getInvalidReport( final Throwable cause ) {
152+
final String errorMessage;
153+
154+
if ( cause instanceof InvalidNamespaceException ) {
155+
errorMessage = buildCauseMessage( cause );
156+
} else {
157+
errorMessage = "Validation succeeded, but an error was found while processing the model. "
158+
+ "This indicates an error in the model validation; please consider reporting this issue including the model "
159+
+ "at https://github.com/OpenManufacturingPlatform/sds-bamm-aspect-meta-model/issues -- "
160+
+ buildCauseMessage( cause );
161+
}
162+
163+
return new ValidationReportBuilder()
164+
.withValidationErrors( List.of( new ValidationError.Processing( errorMessage ) ) )
165+
.buildInvalidReport();
166+
}
167+
168+
private static String buildCauseMessage( final Throwable throwable ) {
157169
final StringBuilder builder = new StringBuilder();
158170
Throwable t = throwable;
159171
while ( t != null ) {

core/sds-aspect-model-validator/src/test/java/io/openmanufacturing/sds/aspectmodel/validation/services/AspectModelValidatorTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,24 @@ public Void visit( final ValidationError.Processing error ) {
242242
} );
243243
}
244244

245+
/**
246+
* Verify that validation of the given aspect model containing a unit not specified in the unit catalog
247+
* and referred with bamm namespace fails.
248+
*/
249+
@ParameterizedTest
250+
@MethodSource( value = "allVersions" )
251+
public void testAspectWithBammNamespaceForCustomUnit( final KnownVersion metaModelVersion ) {
252+
final Try<VersionedModel> invalidAspectModel = TestResources
253+
.getModel( InvalidTestAspect.ASPECT_WITH_BAMM_NAMESPACE_FOR_CUSTOM_UNIT, metaModelVersion );
254+
255+
final ValidationReport report = service.validate( invalidAspectModel );
256+
assertThat( report.conforms() ).isFalse();
257+
258+
final Collection<? extends ValidationError> errors = report.getValidationErrors();
259+
assertThat( errors ).hasSize( 1 );
260+
assertThat( report.getValidationErrors().iterator().next() ).isOfAnyClassIn( ValidationError.Processing.class );
261+
}
262+
245263
@ParameterizedTest
246264
@MethodSource( value = "allVersions" )
247265
public void testAspectWithInvalidMetaModelVersion( final KnownVersion metaModelVersion ) {

core/sds-test-aspect-models/src/main/java/io/openmanufacturing/sds/test/InvalidTestAspect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public enum InvalidTestAspect implements TestModel {
2121
ASPECT_MISSING_PROPERTIES,
2222
ASPECT_WITH_FALSE_POSITIVE_VALIDATION,
2323
ASPECT_WITH_INVALID_VERSION,
24+
ASPECT_WITH_BAMM_NAMESPACE_FOR_CUSTOM_UNIT,
2425
ASPECT_WITH_RECURSIVE_PROPERTY,
2526
INVALID_SYNTAX,
2627
MISSING_ASPECT_DECLARATION;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
2+
#
3+
# See the AUTHORS file(s) distributed with this work for additional
4+
# information regarding authorship.
5+
#
6+
# This Source Code Form is subject to the terms of the Mozilla Public
7+
# License, v. 2.0. If a copy of the MPL was not distributed with this
8+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
#
10+
# SPDX-License-Identifier: MPL-2.0
11+
12+
@prefix : <urn:bamm:io.openmanufacturing:aspect-model:AspectWithBammNamespaceForCustomUnit:1.0.0#> .
13+
@prefix bamm: <urn:bamm:io.openmanufacturing:meta-model:1.0.0#> .
14+
@prefix bamm-c: <urn:bamm:io.openmanufacturing:characteristic:1.0.0#> .
15+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
16+
@prefix unit: <urn:bamm:io.openmanufacturing:unit:1.0.0#> .
17+
18+
:AspectWithBammNamespaceForCustomUnit a bamm:Aspect ;
19+
bamm:name "AspectWithCustomUnit" ;
20+
bamm:properties ( :testProperty ) ;
21+
bamm:operations ( ) .
22+
23+
:testProperty a bamm:Property ;
24+
bamm:name "testProperty" ;
25+
bamm:characteristic :TestQuantifiable .
26+
27+
:TestQuantifiable a bamm-c:Quantifiable ;
28+
bamm:name "TestQuantifiable" ;
29+
bamm:dataType xsd:int ;
30+
bamm-c:unit bamm:normLitrePerMinute .
31+
32+
bamm:normLitrePerMinute a unit:Unit ;
33+
bamm:name "normLitrePerMinute" ;
34+
bamm:preferredName "norm litre per minute"@en ;
35+
unit:quantityKind unit:volumeFlowRate ;
36+
unit:symbol "nl/min" .
37+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
2+
#
3+
# See the AUTHORS file(s) distributed with this work for additional
4+
# information regarding authorship.
5+
#
6+
# This Source Code Form is subject to the terms of the Mozilla Public
7+
# License, v. 2.0. If a copy of the MPL was not distributed with this
8+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
#
10+
# SPDX-License-Identifier: MPL-2.0
11+
12+
@prefix : <urn:bamm:io.openmanufacturing.test:1.0.0#> .
13+
@prefix bamm: <urn:bamm:io.openmanufacturing:meta-model:2.0.0#> .
14+
@prefix bamm-c: <urn:bamm:io.openmanufacturing:characteristic:2.0.0#> .
15+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
16+
@prefix unit: <urn:bamm:io.openmanufacturing:unit:2.0.0#> .
17+
18+
:AspectWithBammNamespaceForCustomUnit a bamm:Aspect ;
19+
bamm:properties ( :testProperty ) ;
20+
bamm:operations ( ) .
21+
22+
:testProperty a bamm:Property ;
23+
bamm:characteristic :TestQuantifiable .
24+
25+
:TestQuantifiable a bamm-c:Quantifiable ;
26+
bamm:dataType xsd:int ;
27+
bamm-c:unit bamm:normLitrePerMinute .
28+
29+
bamm:normLitrePerMinute a bamm:Unit ;
30+
bamm:preferredName "norm litre per minute"@en ;
31+
bamm:quantityKind unit:volumeFlowRate ;
32+
bamm:symbol "nl/min" .

0 commit comments

Comments
 (0)