Skip to content

Commit

Permalink
Merge pull request #351 from bci-oss/update-to-aas-v3.1-lookup-api
Browse files Browse the repository at this point in the history
Feat: Lookup API v3.1 POST Get Shell IDS
  • Loading branch information
tunacicek authored Mar 13, 2024
2 parents 72e44fa + 56e0b97 commit b75a266
Show file tree
Hide file tree
Showing 9 changed files with 860 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ 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.4.1-RC3
### Added
- Added API POST lookup/shellsByAssetLink to retrieve shell ids without base64 encryption.
- Added v3.1.0 postman collection for new API.
## fixed

## 0.4.1-RC2
### Added
- Update Springboot to version 3.2.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -32,9 +31,11 @@
import org.eclipse.tractusx.semantics.aas.registry.api.ShellDescriptorsApiDelegate;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetKind;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetLink;
import org.eclipse.tractusx.semantics.aas.registry.model.GetAllAssetAdministrationShellIdsByAssetLink200Response;
import org.eclipse.tractusx.semantics.aas.registry.model.GetAssetAdministrationShellDescriptorsResult;
import org.eclipse.tractusx.semantics.aas.registry.model.GetSubmodelDescriptorsResult;
import org.eclipse.tractusx.semantics.aas.registry.model.SearchAllShellsByAssetLink200Response;
import org.eclipse.tractusx.semantics.aas.registry.model.ServiceDescription;
import org.eclipse.tractusx.semantics.aas.registry.model.SpecificAssetId;
import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor;
Expand Down Expand Up @@ -176,6 +177,19 @@ public ResponseEntity<GetAllAssetAdministrationShellIdsByAssetLink200Response> g
return new ResponseEntity<>( result, HttpStatus.OK );
}

@Override
public ResponseEntity<SearchAllShellsByAssetLink200Response> searchAllShellsByAssetLink(
Integer limit, String cursor, @RequestHeader String externalSubjectId,List<AssetLink> assetIds) {

if (assetIds == null || assetIds.isEmpty()) {
return new ResponseEntity<>(new SearchAllShellsByAssetLink200Response(), HttpStatus.OK);
}

final var result = shellService.findExternalShellIdsByAssetLinkByExactMatch(
shellMapper.fromAssetLinkApiDto(assetIds), limit, cursor,getExternalSubjectIdOrEmpty(externalSubjectId));
return new ResponseEntity<>( result, HttpStatus.OK );
}

private SpecificAssetId decodeSAID(byte[] encodedId){
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion( JsonInclude.Include.NON_NULL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ public interface ShellMapper {
})
ShellIdentifier fromApiDto(SpecificAssetId apiDto);

@Mappings({
@Mapping(target = "key", source = "name"),
})
ShellIdentifier fromApiDto(AssetLink apiDto);

Set<ShellIdentifier> fromAssetLinkApiDto(List<AssetLink> apiDto);

ShellIdentifierSupplemSemanticReference maptoShellIdentifierSupplemSemanticReference ( Reference supplementalSemanticId );

ShellIdentifierSemanticReference maptoShellIdentifierSemanticReference ( Reference semanticId );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.eclipse.tractusx.semantics.RegistryProperties;
import org.eclipse.tractusx.semantics.aas.registry.model.GetAllAssetAdministrationShellIdsByAssetLink200Response;
import org.eclipse.tractusx.semantics.aas.registry.model.PagedResultPagingMetadata;
import org.eclipse.tractusx.semantics.aas.registry.model.SearchAllShellsByAssetLink200Response;
import org.eclipse.tractusx.semantics.accesscontrol.api.exception.DenyAccessException;
import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId;
import org.eclipse.tractusx.semantics.registry.dto.BatchResultDto;
Expand Down Expand Up @@ -296,6 +297,33 @@ public GetAllAssetAdministrationShellIdsByAssetLink200Response findExternalShell
}
}

@Transactional( readOnly = true )
public SearchAllShellsByAssetLink200Response findExternalShellIdsByAssetLinkByExactMatch( Set<ShellIdentifier> shellIdentifiers,
Integer pageSize, String cursor, String externalSubjectId ) {

pageSize = getPageSize( pageSize );
final String cursorValue = getCursorDecoded( cursor ).orElse( DEFAULT_EXTERNAL_ID );
try {
final List<String> visibleAssetIds;
if ( shellAccessHandler.supportsGranularAccessControl() ) {
visibleAssetIds = fetchAPageOfAasIdsUsingGranularAccessControl( shellIdentifiers, externalSubjectId, cursorValue, pageSize );
} else {
visibleAssetIds = fetchAPageOfAasIdsUsingLegacyAccessControl( shellIdentifiers, externalSubjectId, cursorValue, pageSize );
}

final var assetIdList = visibleAssetIds.stream().limit( pageSize ).toList();
final String nextCursor = getCursorEncoded( visibleAssetIds, assetIdList );
final var response = new SearchAllShellsByAssetLink200Response();
response.setResult( assetIdList );
response.setPagingMetadata( new PagedResultPagingMetadata().cursor( nextCursor ) );
return response;
} catch ( DenyAccessException e ) {
final var response = new SearchAllShellsByAssetLink200Response();
response.setResult( Collections.emptyList() );
return response;
}
}

private List<String> fetchAPageOfAasIdsUsingLegacyAccessControl(
Set<ShellIdentifier> shellIdentifiers, String externalSubjectId, String cursorValue, int pageSize ) {
final var fetchSize = pageSize + 1;
Expand Down
77 changes: 77 additions & 0 deletions backend/src/main/resources/static/aas-registry-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,59 @@ paths:
$ref: '#/components/examples/lookup-shells-by-aas-identifier-response'
x-semanticIds:
- https://admin-shell.io/aas/API/GetAllAssetAdministrationShellIdsByAssetLink/1/0/RC02
/lookup/shellsByAssetLink:
post:
tags:
- Registry and Discovery Interface
summary: Returns a list of Asset Administration Shell ids linked to specific Asset identifiers
operationId: SearchAllShellsByAssetLink
x-semanticIds:
- https://admin-shell.io/aas/API/SearchAllAssetAdministrationShellIdsByAssetLink/3/1
parameters:
- name: limit
in: query
description: The maximum number of elements in the response array
required: false
schema:
type: integer
minimum: 1
- name: cursor
in: query
description: A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue
required: false
schema:
type: string
- $ref: '#/components/parameters/ExternalSubjectIdHeader'
requestBody:
description: Asset identifier key-value-pairs
content:
application/json:
schema:

Check warning on line 774 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[MEDIUM] Additional Properties Too Permissive

Objects should not accept 'additionalProperties' if it is possible

Check warning on line 774 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[MEDIUM] Additional Properties Too Permissive

Objects should not accept 'additionalProperties' if it is possible
type: array
maxItems: 10000
items:
$ref: '#/components/schemas/AssetLink'
examples:
complete:
$ref: '#/components/examples/lookup-shells-by-aas-identifier-post'
responses:
'200':
description: Requested Asset Administration Shell ids
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PagedResult'
- type: object
properties:
result:

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set

Check warning on line 792 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[HIGH] Array Without Maximum Number of Items (v3)

Array schema should have the field 'maxItems' set
type: array
items:
type: string
'400':
$ref: 'https://api.swaggerhub.com/domains/Plattform_i40/Part2-API-Schemas/V3.1.0#/components/responses/bad-request'
default:
$ref: 'https://api.swaggerhub.com/domains/Plattform_i40/Part2-API-Schemas/V3.1.0#/components/responses/default'
/lookup/shells/{aasIdentifier}:
get:
tags:
Expand Down Expand Up @@ -1401,6 +1454,18 @@ components:
maxItems: 10000
items:
$ref: '#/components/schemas/ProtocolInformation_securityAttributes'
AssetLink:

Check warning on line 1457 in backend/src/main/resources/static/aas-registry-openapi.yaml

View workflow job for this annotation

GitHub Actions / Analyze

[MEDIUM] Additional Properties Too Permissive

Objects should not accept 'additionalProperties' if it is possible
properties:
name:
type: string
minLength: 1
maxLength: 64
pattern: "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
value:
type: string
minLength: 1
maxLength: 2000
pattern: "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
SpecificAssetId:
type: object
allOf:
Expand Down Expand Up @@ -1653,6 +1718,18 @@ components:
description: Incoming external_subject_id/bpn
# format: byte
examples:
lookup-shells-by-aas-identifier-post:
value:
[
{
"name": "globalAssetId",
"value": "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8"
},
{
"name": "PartInstanceID",
"value": "24975539203421"
}
]
lookup-shells-by-aas-identifier-query:
value:
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public abstract class AbstractAssetAdministrationShellApi {
protected static final String SHELL_BASE_PATH = "/api/v3.0/shell-descriptors";
protected static final String SINGLE_SHELL_BASE_PATH = "/api/v3.0/shell-descriptors/{aasIdentifier}";
protected static final String LOOKUP_SHELL_BASE_PATH = "/api/v3.0/lookup/shells";
protected static final String LOOKUP_SHELL_BASE_PATH_POST = "/api/v3.0/lookup/shellsByAssetLink";
protected static final String SINGLE_LOOKUP_SHELL_BASE_PATH = "/api/v3.0/lookup/shells/{aasIdentifier}";
protected static final String SUB_MODEL_BASE_PATH = "/api/v3.0/shell-descriptors/{aasIdentifier}/submodel-descriptors";
protected static final String SINGLE_SUB_MODEL_BASE_PATH = "/api/v3.0/shell-descriptors/{aasIdentifier}/submodel-descriptors/{submodelIdentifier}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetLink;
import org.eclipse.tractusx.semantics.aas.registry.model.LangStringTextType;
import org.eclipse.tractusx.semantics.aas.registry.model.SpecificAssetId;
import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor;
Expand Down Expand Up @@ -774,6 +776,73 @@ public void testFindExternalShellIdsBySpecificAssetIdsExpectSuccess() throws Exc
.andExpect( jsonPath( "$.result", hasSize( 0 ) ) );
}


@Test
public void testFindExternalShellIdsByAssetLinkExpectSuccess() throws Exception {

AssetAdministrationShellDescriptor shellPayload1 = TestUtil.createCompleteAasDescriptor();
shellPayload1.setId( UUID.randomUUID().toString() );
performShellCreateRequest( mapper.writeValueAsString( shellPayload1 ) );

AssetAdministrationShellDescriptor shellPayload2 = TestUtil.createCompleteAasDescriptor();
shellPayload2.setId( UUID.randomUUID().toString() );
performShellCreateRequest( mapper.writeValueAsString( shellPayload2 ) );

AssetLink assetLink1 = TestUtil.createAssetLink();
List<AssetLink> list1 = new ArrayList<>();
list1.add( assetLink1 );

mvc.perform(
MockMvcRequestBuilders
.post( LOOKUP_SHELL_BASE_PATH_POST )
.queryParam( "limit", "1" )
.header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() )
.accept( MediaType.APPLICATION_JSON )
.contentType( MediaType.APPLICATION_JSON )
.content( mapper.writeValueAsBytes(list1) )
.with( jwtTokenFactory.allRoles() )
)
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.paging_metadata.cursor" ).exists() );

// Test first shell match with single assetLink

AssetLink assetLink2 = TestUtil.createAssetLink( "identifier99KeyExample", "identifier99ValueExample" );
List<AssetLink> list2 = new ArrayList<>();
list2.add( assetLink2 );
mvc.perform(
MockMvcRequestBuilders
.post( LOOKUP_SHELL_BASE_PATH_POST )
.queryParam( "limit", "10" )
.accept( MediaType.APPLICATION_JSON )
.contentType( MediaType.APPLICATION_JSON )
.content( mapper.writeValueAsBytes(list2) )
.with( jwtTokenFactory.allRoles() )
)
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.paging_metadata.cursor" ).doesNotExist() );

// Test first and second shell match with common assetLink

AssetLink assetLink3 = TestUtil.createAssetLink( "commonAssetIdKey", "commonAssetIdValue" );
List<AssetLink> list3 = new ArrayList<>();
list3.add( assetLink3 );
mvc.perform(
MockMvcRequestBuilders
.post( LOOKUP_SHELL_BASE_PATH_POST )
.queryParam( "limit", "10" )
.accept( MediaType.APPLICATION_JSON )
.contentType( MediaType.APPLICATION_JSON )
.content( mapper.writeValueAsBytes(list3) )
.with( jwtTokenFactory.allRoles() )
)
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.result", hasSize( 0 ) ) );
}

@Test
public void testFindExternalShellIdByGlobalAssetIdExpectSuccess() throws Exception {

Expand Down Expand Up @@ -805,6 +874,39 @@ public void testFindExternalShellIdByGlobalAssetIdExpectSuccess() throws Excepti
.andExpect( jsonPath( "$.result", contains( shellPayload.getId() ) ) );
}

@Test
public void testFindExternalShellIdByGlobalAssetIdAssetLinkExpectSuccess() throws Exception {

String globalAssetId = UUID.randomUUID().toString();

AssetAdministrationShellDescriptor shellPayload = TestUtil.createCompleteAasDescriptor();
shellPayload.setId( UUID.randomUUID().toString() );
shellPayload.setGlobalAssetId( globalAssetId );
String payload = mapper.writeValueAsString( shellPayload );
performShellCreateRequest(payload );

// for lookup global asset id is handled as AssetLink
AssetLink SAGlobal = TestUtil.createAssetLink("globalAssetId",globalAssetId);

List<AssetLink> list = new ArrayList<>();
list.add( SAGlobal );

mvc.perform(
MockMvcRequestBuilders
.post( LOOKUP_SHELL_BASE_PATH_POST )
.header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() )
.accept( MediaType.APPLICATION_JSON )
.contentType( MediaType.APPLICATION_JSON )
.content( mapper.writeValueAsBytes(list) )
.with( jwtTokenFactory.allRoles() )
)
.andDo( MockMvcResultHandlers.print() )
.andExpect( status().isOk() )
.andExpect( jsonPath( "$.result", hasSize( 1 ) ) )
// ensure that only three results match
.andExpect( jsonPath( "$.result", contains( shellPayload.getId() ) ) );
}

@Test
public void testFindExternalShellIdsWithoutProvidingQueryParametersExpectEmptyResult() throws Exception {
// prepare the data set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetKind;
import org.eclipse.tractusx.semantics.aas.registry.model.AssetLink;
import org.eclipse.tractusx.semantics.aas.registry.model.Endpoint;
import org.eclipse.tractusx.semantics.aas.registry.model.Key;
import org.eclipse.tractusx.semantics.aas.registry.model.KeyTypes;
Expand Down Expand Up @@ -242,6 +243,20 @@ public static SpecificAssetId createSpecificAssetId() {
return specificAssetId1;
}

public static AssetLink createAssetLink() {
AssetLink assetLink = new AssetLink();
assetLink.setName( "identifier1KeyExample" );
assetLink.setValue( "identifier1ValueExample" );
return assetLink;
}

public static AssetLink createAssetLink(String name, String value) {
AssetLink assetLink = new AssetLink();
assetLink.setName( name );
assetLink.setValue( value );
return assetLink;
}

public static SpecificAssetId createSpecificAssetId( String name, String value, List<String> externalSubjectIds ) {
SpecificAssetId specificAssetId1 = new SpecificAssetId();
specificAssetId1.setName( name );
Expand Down
Loading

0 comments on commit b75a266

Please sign in to comment.