Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
GitHub Actions Bot committed Jan 12, 2024
2 parents d9dcc63 + 84b65c1 commit 38a67d3
Show file tree
Hide file tree
Showing 17 changed files with 3,125 additions and 103 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.2.16
### Added

### Fixed
- Spring Boot version updated to 3.1.6 to fix CVE-2023-34053
- update Logback version to fix CVE-2023-6378
- Enabled state transitions without modification of models.

## 0.2.15
### Added
- Added helm upgrade feature into helm test
Expand Down
120 changes: 60 additions & 60 deletions DEPENDENCIES

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,12 @@ public ResponseEntity<ErrorResponse> handleInvalidStateTransitionException( fina
.path( request.getRequestURI() ) ), HttpStatus.BAD_REQUEST );
}

@ExceptionHandler( org.apache.jena.atlas.web.HttpException.class )
public ResponseEntity<ErrorResponse> handleHttpException( final HttpServletRequest request, final org.apache.jena.atlas.web.HttpException httpException ) {
return new ResponseEntity<>( new ErrorResponse()
.error( new Error()
.message( httpException.getResponse() )
.path( request.getRequestURI() ) ), HttpStatus.BAD_REQUEST );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ public ResponseEntity<SemanticModel> modifyModel( final SemanticModelType type,
return new ResponseEntity<>( resultingModel, HttpStatus.OK );
}

@Override
public ResponseEntity<SemanticModel> updateModel( String urn,
SemanticModelStatus status ) {
SemanticModel semanticModel = persistenceLayer.updateModel( urn , status );
return new ResponseEntity<>( semanticModel, HttpStatus.OK );

}

@Override
public ResponseEntity<Void> getModelOpenApi( final String modelId, final String baseUrl ) {
final String openApiJson = sdkHelper.getOpenApiDefinitionJson( modelId, baseUrl );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,6 @@ public interface PersistenceLayer {
boolean echo();

public SemanticModelList findModelListByUrns(List<AspectModelUrn> urns, int page, int pageSize);

SemanticModel updateModel(String urn, SemanticModelStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,64 +104,94 @@ public SemanticModel getModel( final AspectModelUrn urn ) {
return findByUrn( urn );
}

@Override
public SemanticModel updateModel( String urn, SemanticModelStatus status ) {

SemanticModel semanticModel = Optional.ofNullable( findByUrn(
AspectModelUrn.fromUrn( urn ) ) ).orElseThrow( () -> new IllegalArgumentException(
String.format( "Invalid URN %s",
ModelPackageUrn.fromUrn( urn ).getUrn() ) ) );
ModelPackageStatus persistedModelStatus = ModelPackageStatus.valueOf(
semanticModel.getStatus().name() );

final Model model = findContainingModelByUrn( urn );

final AspectModelUrn modelUrn = sdsSdk.getAspectUrn( model );

validateStatus( status, model, modelUrn, persistedModelStatus );

updateModel( status, modelUrn, model );

return findByUrn( modelUrn );
}

@Override
public SemanticModel save( SemanticModelType type, String newModel, SemanticModelStatus status ) {
final Model rdfModel = sdsSdk.load( newModel.getBytes( StandardCharsets.UTF_8 ) );
final AspectModelUrn modelUrn = sdsSdk.getAspectUrn( rdfModel );
Optional<ModelPackage> existsByPackage = findByPackageByUrn( ModelPackageUrn.fromUrn( modelUrn ) );

if ( existsByPackage.isPresent() ) {
ModelPackageStatus persistedModelStatus = existsByPackage.get().getStatus();
final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() );
switch ( persistedModelStatus ) {
case DRAFT:
if ( desiredModelStatus.equals( ModelPackageStatus.RELEASED ) && !hasReferenceToDraftPackage( modelUrn, rdfModel ) ) {
throw new InvalidStateTransitionException( "It is not allowed to release an aspect that has dependencies in DRAFT state." );
} else if ( desiredModelStatus.equals( ModelPackageStatus.STANDARDIZED ) ) {
throw new IllegalArgumentException(
String.format( "The package %s is in status %s. Only a transition to RELEASED or DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
break;
case RELEASED:
// released models can only be updated when the new state is deprecated or standardized
if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) || desiredModelStatus.equals( ModelPackageStatus.STANDARDIZED ) ) {
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
} else {
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified. Only a transition to STANDARDIZED or DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
break;
case STANDARDIZED:
if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) ) {
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
} else {
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
break;
case DEPRECATED:
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
validateStatus( status, rdfModel, modelUrn, existsByPackage.get().getStatus() );
}

sdsSdk.validate( rdfModel, this::findContainingModelByUrn, type );

Model rdfModelOriginal = sdsSdk.load( newModel.getBytes( StandardCharsets.UTF_8 ) );

updateModel( status, modelUrn, rdfModelOriginal );

return findByUrn( modelUrn );
}

private void updateModel( SemanticModelStatus status, AspectModelUrn modelUrn, Model rdfModelOriginal ) {
final Resource rootResource = ResourceFactory.createResource( modelUrn.getUrnPrefix() );
rdfModelOriginal.add( rootResource, SparqlQueries.STATUS_PROPERTY,
ModelPackageStatus.valueOf( status.name() ).toString() );

try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) {
rdfConnection.update( new UpdateBuilder().addInsert( rdfModelOriginal ).build() );
}
return findByUrn( modelUrn );
}


private void validateStatus( SemanticModelStatus status, Model rdfModel, AspectModelUrn modelUrn, ModelPackageStatus persistedModelStatus ) {
final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() );
switch ( persistedModelStatus ) {
case DRAFT:
if ( desiredModelStatus.equals( ModelPackageStatus.RELEASED ) && !hasReferenceToDraftPackage( modelUrn, rdfModel ) ) {
throw new InvalidStateTransitionException( "It is not allowed to release an aspect that has dependencies in DRAFT state." );
} else if ( desiredModelStatus.equals( ModelPackageStatus.STANDARDIZED ) ) {
throw new IllegalArgumentException(
String.format( "The package %s is in status %s. Only a transition to RELEASED or DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
break;
case RELEASED:
// released models can only be updated when the new state is deprecated or standardized
if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) || desiredModelStatus.equals( ModelPackageStatus.STANDARDIZED ) ) {
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
} else {
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified. Only a transition to STANDARDIZED or DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
break;
case STANDARDIZED:
if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) ) {
deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) );
} else {
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
break;
case DEPRECATED:
throw new IllegalArgumentException(
String.format( "The package %s is already in status %s and cannot be modified.",
ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) );
}
}

@Override
Expand Down
26 changes: 26 additions & 0 deletions backend/src/main/resources/static/semantic-hub-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,32 @@ paths:
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
put:
tags:
- SemanticHub
summary: Updates an existing Semantic Model status by the URN
operationId: updateModel
parameters:
- in: path
name: urn
schema:
type: string
required: true
- in: query
name: status
schema:
$ref: '#/components/schemas/SemanticModelStatus'
responses:
'200':
$ref: '#/components/responses/SemanticModel'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
tags:
- SemanticHub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ public MockHttpServletRequestBuilder post( String payload, String status ) {
.with(jwtTokenFactory.allRoles());
}

public MockHttpServletRequestBuilder update( String urn, String status ) {
return MockMvcRequestBuilders.put( "/api/v1/models/{urn}",urn)
.queryParam( "status", status)
.accept( MediaType.APPLICATION_JSON )
.with(jwtTokenFactory.allRoles());
}

public MockHttpServletRequestBuilder put( String payload, String status ) {
String type = "SAMM";
return MockMvcRequestBuilders.put( "/api/v1/models")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,101 @@ public void testDependentModelBAMM() throws Exception {
.andExpect( status().isOk() );
}

@Test
@DisplayName( "test model status update by URN" )
public void testModelStatusUpdateByURN() throws Exception {
String urnPrefix = "urn:bamm:io.catenax.shared.contact_information:2.0.0#ContactInformation";

//Given
mvc.perform( postBAMM( TestUtils.getTTLFile( "ContactInformation-2.0.0.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );

//When & Then
mvc.perform( update( urnPrefix, "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.status", is( "DRAFT" ) ) );

mvc.perform( update( urnPrefix, "STANDARDIZED" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isBadRequest() );

mvc.perform( update( urnPrefix, "RELEASED" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.status", is( "RELEASED" ) ) );

// transition from released to draft is not allowed
mvc.perform( update( urnPrefix, "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isBadRequest() )
.andExpect( jsonPath( "$.error.message", containsString(
"already in status RELEASED and cannot be modified. Only a transition to STANDARDIZED or DEPRECATED is possible." ) ) );

// transition from released to standardized is allowed
mvc.perform( update( urnPrefix, "STANDARDIZED" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.status", is( "STANDARDIZED" ) ) );

// transition from standardized to draft is not allowed
mvc.perform( update( urnPrefix, "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( jsonPath( "$.error.message", is(
"The package urn:bamm:io.catenax.shared.contact_information:2.0.0# is already in status STANDARDIZED and cannot be modified. Only a transition to DEPRECATED is possible." ) ) )
.andExpect( status().isBadRequest() );

// transition from standardized to deprecated is allowed
mvc.perform( update( urnPrefix, "DEPRECATED" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.status", is( "DEPRECATED" ) ) );

}

@Test
public void testInvalidDependentModelBAMMModel() throws Exception {

//Given
mvc.perform( postBAMM( TestUtils.getTTLFile( "ContactInformation-2.0.0.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );
mvc.perform( postBAMM( TestUtils.getTTLFile( "Pcf-3.0.0.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );

mvc.perform( postBAMM( TestUtils.getTTLFile( "PhysicalDimensions-2.0.0.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );

mvc.perform( postBAMM( TestUtils.getTTLFile( "SerialPartTypization-2.0.0.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );

//When & Then
mvc.perform( postBAMM( TestUtils.getTTLFile( "TransmissionPass.ttl" ), "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isBadRequest() )
.andExpect( jsonPath( "$.error.message", containsString("Bad IRI" ) ) );
}


@Test
public void testInvalidSAMMModel() throws Exception {
//Given
mvc.perform( MockMvcRequestBuilders.post( "/api/v1/models" )
.queryParam( "type", "SAMM" )
.queryParam( "status", "DRAFT" )
.accept( MediaType.APPLICATION_JSON )
.contentType( MediaType.TEXT_PLAIN )
.content( TestUtils.getTTLFile( "DigitalProductPassport-samm.ttl" ) )
.with( jwtTokenFactory.allRoles() ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isBadRequest() )
.andExpect( jsonPath( "$.error.message", containsString( "Bad IRI" ) ) );
}

private static String toMovementUrn(String urn){
return urn + "Movement";
}
Expand Down Expand Up @@ -964,4 +1059,15 @@ void testGeneratePngForSAMMModel() throws Exception {
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() );
}

@Test
public void testUpdateModelStatusByInvalidURN() throws Exception {
String urnPrefix = "urn:bamm:io.catenax.shared.invalid:2.0.0#InvalidInformation";

mvc.perform( update( urnPrefix, "DRAFT" ) )
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isBadRequest() )
.andExpect( jsonPath( "$.error.message", containsString( "Invalid URN urn" ) ) );
}

}
Loading

0 comments on commit 38a67d3

Please sign in to comment.