diff --git a/example/apiCollection/Aas Registry/Get All ShellDescriptors.bru b/example/apiCollection/Aas Registry/Get All ShellDescriptors.bru index 1fc57910..1adb4ca5 100644 --- a/example/apiCollection/Aas Registry/Get All ShellDescriptors.bru +++ b/example/apiCollection/Aas Registry/Get All ShellDescriptors.bru @@ -5,7 +5,7 @@ meta { } get { - url: {{DataEngineBaseUrl}}/shell-descriptors?limit&cursor + url: {{DataEngineBaseUrl}}/shell-descriptors?limit=&cursor= body: none auth: inherit } diff --git a/example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru b/example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru new file mode 100644 index 00000000..ce39af77 --- /dev/null +++ b/example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru @@ -0,0 +1,21 @@ +meta { + name: Get Submodel Descriptor by submodelId and aasId + type: http + seq: 6 +} + +get { + url: {{DataEngineBaseUrl}}/shell-descriptors/:aasIdentifier/submodel-descriptors/:submodelIdentifier + body: none + auth: inherit +} + +params:path { + submodelIdentifier: {{submodelIdentifierCarbonFootprint-1}} + aasIdentifier: {{aasIdentifier-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru b/example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru new file mode 100644 index 00000000..e918ec90 --- /dev/null +++ b/example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru @@ -0,0 +1,20 @@ +meta { + name: Get Submodel Descriptors by aasId + type: http + seq: 5 +} + +get { + url: {{DataEngineBaseUrl}}/shell-descriptors/:aasIdentifier/submodel-descriptors + body: none + auth: inherit +} + +params:path { + aasIdentifier: {{aasIdentifier-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product1/Get Shell By Id.bru b/example/apiCollection/Aas Repository/Product1/Get Shell By Id.bru index 2f49754b..9f764e9f 100644 --- a/example/apiCollection/Aas Repository/Product1/Get Shell By Id.bru +++ b/example/apiCollection/Aas Repository/Product1/Get Shell By Id.bru @@ -1,7 +1,7 @@ meta { name: Get Shell By Id type: http - seq: 1 + seq: 2 } get { diff --git a/example/apiCollection/Aas Repository/Product1/Get Shells By Asset Id - Product1.bru b/example/apiCollection/Aas Repository/Product1/Get Shells By Asset Id - Product1.bru index f55bf5ff..9c352a3e 100644 --- a/example/apiCollection/Aas Repository/Product1/Get Shells By Asset Id - Product1.bru +++ b/example/apiCollection/Aas Repository/Product1/Get Shells By Asset Id - Product1.bru @@ -1,7 +1,7 @@ meta { name: Get Shells By Asset Id - Product1 type: http - seq: 2 + seq: 3 } get { @@ -12,7 +12,7 @@ get { params:query { assetIds: {{assetId-product1}} - idShort: + idShort: limit: cursor: } diff --git a/example/apiCollection/Aas Repository/Product1/Get Submodel Ref By Id.bru b/example/apiCollection/Aas Repository/Product1/Get Submodel Ref By Id.bru index fad39781..737631d4 100644 --- a/example/apiCollection/Aas Repository/Product1/Get Submodel Ref By Id.bru +++ b/example/apiCollection/Aas Repository/Product1/Get Submodel Ref By Id.bru @@ -1,7 +1,7 @@ meta { name: Get Submodel Ref By Id type: http - seq: 2 + seq: 4 } get { diff --git a/example/apiCollection/Aas Repository/Product1/Get Submodel by submodelId and aaslId.bru b/example/apiCollection/Aas Repository/Product1/Get Submodel by submodelId and aaslId.bru new file mode 100644 index 00000000..2c901ad8 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product1/Get Submodel by submodelId and aaslId.bru @@ -0,0 +1,26 @@ +meta { + name: Get Submodel by submodelId and aaslId + type: http + seq: 5 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + submodelIdentifier: {{submodelIdentifierNameplate-1}} + aasIdentifier: {{aasIdentifier-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product1/Get Submodel-element by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product1/Get Submodel-element by idshort and aasID.bru new file mode 100644 index 00000000..a333a484 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product1/Get Submodel-element by idshort and aasID.bru @@ -0,0 +1,27 @@ +meta { + name: Get Submodel-element by idshort and aasID + type: http + seq: 7 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath?level=&extent= + body: none + auth: inherit +} + +params:query { + level: + extent: +} + +params:path { + idShortPath: ManufacturerName + aasIdentifier: {{aasIdentifier-1}} + submodelIdentifier: {{submodelIdentifierNameplate-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product1/Get Submodel-elements by aasID.bru b/example/apiCollection/Aas Repository/Product1/Get Submodel-elements by aasID.bru new file mode 100644 index 00000000..38af3607 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product1/Get Submodel-elements by aasID.bru @@ -0,0 +1,28 @@ +meta { + name: Get Submodel-elements by aasID + type: http + seq: 6 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements + body: none + auth: inherit +} + +params:query { + ~limit: + ~cursor: + ~level: + ~extent: +} + +params:path { + aasIdentifier: {{aasIdentifier-1}} + submodelIdentifier: {{submodelIdentifierNameplate-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product1/Get file attachment by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product1/Get file attachment by idshort and aasID.bru new file mode 100644 index 00000000..5969a8de --- /dev/null +++ b/example/apiCollection/Aas Repository/Product1/Get file attachment by idshort and aasID.bru @@ -0,0 +1,22 @@ +meta { + name: Get file attachment by idshort and aasID + type: http + seq: 8 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath/attachment + body: none + auth: inherit +} + +params:path { + idShortPath: Documents%255B0%255D.DocumentVersions%255B0%255D.DigitalFiles%255B0%255D + aasIdentifier: {{aasIdentifier-1}} + submodelIdentifier: {{submodelIdentifierHandoverDocumentation-1}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product2/Get Shells By Asset Id and IdShort - Product2.bru b/example/apiCollection/Aas Repository/Product2/Get Shells By Asset Id and IdShort - Product2.bru index 5b04aa38..1aef7d90 100644 --- a/example/apiCollection/Aas Repository/Product2/Get Shells By Asset Id and IdShort - Product2.bru +++ b/example/apiCollection/Aas Repository/Product2/Get Shells By Asset Id and IdShort - Product2.bru @@ -1,7 +1,7 @@ meta { name: Get Shells By Asset Id and IdShort - Product2 type: http - seq: 7 + seq: 5 } get { @@ -25,4 +25,4 @@ settings { docs { Retrieves shells filtered by specific asset ID and idShort. The idShort value is URL-encoded in the collection pre-request script. -} \ No newline at end of file +} diff --git a/example/apiCollection/Aas Repository/Product2/Get Submodel Ref By Id.bru b/example/apiCollection/Aas Repository/Product2/Get Submodel Ref By Id.bru index 43cd8eae..65019fbd 100644 --- a/example/apiCollection/Aas Repository/Product2/Get Submodel Ref By Id.bru +++ b/example/apiCollection/Aas Repository/Product2/Get Submodel Ref By Id.bru @@ -1,7 +1,7 @@ meta { name: Get Submodel Ref By Id type: http - seq: 3 + seq: 4 } get { diff --git a/example/apiCollection/Aas Repository/Product2/Get Submodel by submodelId and aasId.bru b/example/apiCollection/Aas Repository/Product2/Get Submodel by submodelId and aasId.bru new file mode 100644 index 00000000..9b640de3 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product2/Get Submodel by submodelId and aasId.bru @@ -0,0 +1,26 @@ +meta { + name: Get Submodel by submodelId and aasId + type: http + seq: 6 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + submodelIdentifier: {{submodelIdentifierNameplate-1}} + aasIdentifier: {{aasIdentifier-2}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product2/Get Submodel-element by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product2/Get Submodel-element by idshort and aasID.bru new file mode 100644 index 00000000..40b561e6 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product2/Get Submodel-element by idshort and aasID.bru @@ -0,0 +1,27 @@ +meta { + name: Get Submodel-element by idshort and aasID + type: http + seq: 8 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath?extent=&level= + body: none + auth: inherit +} + +params:query { + extent: + level: +} + +params:path { + idShortPath: ManufacturerName + aasIdentifier: {{aasIdentifier-2}} + submodelIdentifier: {{submodelIdentifierNameplate-2}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product2/Get Submodel-elements by aasID.bru b/example/apiCollection/Aas Repository/Product2/Get Submodel-elements by aasID.bru new file mode 100644 index 00000000..155f570f --- /dev/null +++ b/example/apiCollection/Aas Repository/Product2/Get Submodel-elements by aasID.bru @@ -0,0 +1,28 @@ +meta { + name: Get Submodel-elements by aasID + type: http + seq: 7 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements + body: none + auth: inherit +} + +params:query { + ~limit: + ~cursor: + ~level: + ~extent: +} + +params:path { + aasIdentifier: {{aasIdentifier-2}} + submodelIdentifier: {{submodelIdentifierNameplate-2}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product2/Get file attachment by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product2/Get file attachment by idshort and aasID.bru new file mode 100644 index 00000000..42ca2e9e --- /dev/null +++ b/example/apiCollection/Aas Repository/Product2/Get file attachment by idshort and aasID.bru @@ -0,0 +1,22 @@ +meta { + name: Get file attachment by idshort and aasID + type: http + seq: 9 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath/attachment + body: none + auth: inherit +} + +params:path { + idShortPath: Markings[0].MarkingFile + aasIdentifier: {{aasIdentifier-2}} + submodelIdentifier: {{submodelIdentifierNameplate-2}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product3/Get Submodel by submodelId and aasId.bru b/example/apiCollection/Aas Repository/Product3/Get Submodel by submodelId and aasId.bru new file mode 100644 index 00000000..3fdb6341 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product3/Get Submodel by submodelId and aasId.bru @@ -0,0 +1,26 @@ +meta { + name: Get Submodel by submodelId and aasId + type: http + seq: 4 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + submodelIdentifier: {{submodelIdentifierNameplate-3}} + aasIdentifier: {{aasIdentifier-3}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product3/Get Submodel-element by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product3/Get Submodel-element by idshort and aasID.bru new file mode 100644 index 00000000..393fcf7b --- /dev/null +++ b/example/apiCollection/Aas Repository/Product3/Get Submodel-element by idshort and aasID.bru @@ -0,0 +1,27 @@ +meta { + name: Get Submodel-element by idshort and aasID + type: http + seq: 6 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + idShortPath: ManufacturerName + aasIdentifier: {{aasIdentifier-3}} + submodelIdentifier: {{submodelIdentifierNameplate-3}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product3/Get Submodel-elements by aasID.bru b/example/apiCollection/Aas Repository/Product3/Get Submodel-elements by aasID.bru new file mode 100644 index 00000000..0e3e5ef3 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product3/Get Submodel-elements by aasID.bru @@ -0,0 +1,28 @@ +meta { + name: Get Submodel-elements by aasID + type: http + seq: 5 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements + body: none + auth: inherit +} + +params:query { + ~limit: + ~cursor: + ~level: + ~extent: +} + +params:path { + aasIdentifier: {{aasIdentifier-3}} + submodelIdentifier: {{submodelIdentifierNameplate-3}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/Aas Repository/Product3/Get file attachment by idshort and aasID.bru b/example/apiCollection/Aas Repository/Product3/Get file attachment by idshort and aasID.bru new file mode 100644 index 00000000..113f0a19 --- /dev/null +++ b/example/apiCollection/Aas Repository/Product3/Get file attachment by idshort and aasID.bru @@ -0,0 +1,22 @@ +meta { + name: Get file attachment by idshort and aasID + type: http + seq: 7 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath/attachment + body: none + auth: inherit +} + +params:path { + idShortPath: Markings[0].MarkingFile + aasIdentifier: {{aasIdentifier-3}} + submodelIdentifier: {{submodelIdentifierNameplate-3}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/example/apiCollection/collection.bru b/example/apiCollection/collection.bru index 669943d7..f7188433 100644 --- a/example/apiCollection/collection.bru +++ b/example/apiCollection/collection.bru @@ -13,6 +13,8 @@ vars:pre-request { assetId-product1: {"name":"SerialNumber","value":"SN-FMABC1234-9804820"} assetId-product2: {"name":"SerialNumber","value":"SN-FMABC1238-9804821"} idShort: Product2 + submodelIdentifierNameplate-2: https://mm-software.com/submodel/{{productId-2}}/Nameplate + submodelIdentifierNameplate-3: https://mm-software.com/submodel/{{productId-3}}/Nameplate } script:pre-request { @@ -26,6 +28,8 @@ script:pre-request { 'aasIdentifier-3', 'submodelIdentifierMaintenanceInstructions-1', 'submodelIdentifierNameplate-1', + 'submodelIdentifierNameplate-2', + 'submodelIdentifierNameplate-3', 'submodelIdentifierHandoverDocumentation-1', 'submodelIdentifierTechnicalData-1', 'submodelIdentifierCarbonFootprint-1', diff --git a/example/basyx/aas-env.properties b/example/basyx/aas-env.properties index 70ef7e28..7bd1c243 100644 --- a/example/basyx/aas-env.properties +++ b/example/basyx/aas-env.properties @@ -7,10 +7,10 @@ basyx.submodelrepository.feature.registryintegration=http://sm-template-registry spring.servlet.multipart.max-file-size=128MB spring.servlet.multipart.max-request-size=128MB basyx.backend=MongoDB -spring.data.mongodb.host=mongo -spring.data.mongodb.port=27017 -spring.data.mongodb.database=aasenvironment -spring.data.mongodb.authentication-database=admin -spring.data.mongodb.username=mongoAdmin -spring.data.mongodb.password=mongoPassword +spring.mongodb.host=mongo +spring.mongodb.port=27017 +spring.mongodb.database=aasenvironment +spring.mongodb.authentication-database=admin +spring.mongodb.username=mongoAdmin +spring.mongodb.password=mongoPassword diff --git a/example/docker-compose.yml b/example/docker-compose.yml index 7187fe20..41c85af7 100644 --- a/example/docker-compose.yml +++ b/example/docker-compose.yml @@ -145,7 +145,7 @@ services: - twinengine-network template-repository: - image: eclipsebasyx/aas-environment:2.0.0-milestone-11 + image: eclipsebasyx/aas-environment:2.0.0-milestone-15 container_name: template-repository volumes: - ./aas:/application/aas @@ -162,28 +162,28 @@ services: - twinengine-network aas-template-registry: - image: eclipsebasyx/aas-registry-log-mongodb:2.0.0-milestone-11 + image: eclipsebasyx/aas-registry-log-mongodb:2.0.0-milestone-15 container_name: aas-template-registry restart: always depends_on: - mongo environment: - - SPRING_DATA_MONGODB_URI=mongodb://mongoAdmin:mongoPassword@mongo:27017 + - SPRING_MONGODB_URI=mongodb://mongoAdmin:mongoPassword@mongo:27017 networks: - twinengine-network sm-template-registry: - image: eclipsebasyx/submodel-registry-log-mongodb:2.0.0-milestone-11 + image: eclipsebasyx/submodel-registry-log-mongodb:2.0.0-milestone-15 container_name: sm-template-registry depends_on: - mongo environment: - - SPRING_DATA_MONGODB_URI=mongodb://mongoAdmin:mongoPassword@mongo:27017 + - SPRING_MONGODB_URI=mongodb://mongoAdmin:mongoPassword@mongo:27017 networks: - twinengine-network aas-web-ui: - image: eclipsebasyx/aas-gui:v2-260526 + image: eclipsebasyx/aas-gui:v2-260801 container_name: aas-ui volumes: - ./logo:/usr/src/app/dist/Logo diff --git a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRegistry/ShellDescriptorControllerTests.cs b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRegistry/ShellDescriptorControllerTests.cs index d66b1f35..84004e5d 100644 --- a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRegistry/ShellDescriptorControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRegistry/ShellDescriptorControllerTests.cs @@ -7,10 +7,14 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasEnvironment.Providers; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry.Providers; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry; using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients; using AAS.TwinEngine.DataEngine.ModuleTests.Common; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; +using AasCore.Aas3_1; + using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; @@ -23,12 +27,14 @@ public abstract class ShellDescriptorControllerTests : IDisposable { private readonly ConfigTestFactory _factory; private readonly ITemplateProvider _mockTemplateProvider; + private readonly ISubmodelDescriptorProvider _mockSubmodelDescriptorProvider; private readonly HttpClient _client; private readonly ICreateClient _httpClientFactory; protected ShellDescriptorControllerTests(string configDir) { _mockTemplateProvider = Substitute.For(); + _mockSubmodelDescriptorProvider = Substitute.For(); var mockPluginManifestProvider = Substitute.For(); var mockPluginManifestConflictHandler = Substitute.For(); _httpClientFactory = Substitute.For(); @@ -38,6 +44,7 @@ protected ShellDescriptorControllerTests(string configDir) _ = services.AddSingleton(_httpClientFactory); _ = services.AddSingleton(mockPluginManifestProvider); _ = services.AddSingleton(_mockTemplateProvider); + _ = services.AddSingleton(_mockSubmodelDescriptorProvider); _ = services.AddSingleton(mockPluginManifestConflictHandler); }); @@ -437,6 +444,130 @@ public async Task GetShellDescriptorById_ValidAasIdentifiers_DoesNotReturn400Asy #endregion + [Fact] + public async Task GetAllSubmodelDescriptorsByAasIdAsync_ReturnsOkAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string SubmodelKey = "Nameplate"; + const string ProductId = "1170_1160_3052_6568"; + const string SubmodelTemplateId = "https://admin-shell.io/idta/SubmodelTemplate/DigitalNameplate/3/0"; + var submodelId = $"https://mm-software.com/submodel/{ProductId}/{SubmodelKey}"; + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns(_ => + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, SubmodelKey)], + null) + ]); + + _ = _mockSubmodelDescriptorProvider + .GetDataForSubmodelDescriptorByIdAsync(SubmodelTemplateId, Arg.Any()) + .Returns(new SubmodelDescriptor { Id = submodelId, Endpoints = [] }); + + // Act + var response = await _client.GetAsync($"/shell-descriptors/{AasIdentifier}/submodel-descriptors"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var json = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(json); + var result = json["result"]?.AsArray(); + Assert.NotNull(result); + _ = Assert.Single(result); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasIdAsync_ReturnsOkAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string SubmodelKey = "Nameplate"; + const string ProductId = "1170_1160_3052_6568"; + const string SubmodelTemplateId = "https://admin-shell.io/idta/SubmodelTemplate/DigitalNameplate/3/0"; + var submodelId = $"https://mm-software.com/submodel/{ProductId}/{SubmodelKey}"; + var encodedSubmodelId = EncodeBase64Url(submodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns(_ => + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, SubmodelKey)], + null) + ]); + + _ = _mockSubmodelDescriptorProvider + .GetDataForSubmodelDescriptorByIdAsync(SubmodelTemplateId, Arg.Any()) + .Returns(new SubmodelDescriptor { Id = submodelId, Endpoints = [] }); + + // Act + var response = await _client.GetAsync($"/shell-descriptors/{AasIdentifier}/submodel-descriptors/{encodedSubmodelId}"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var json = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(json); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasIdAsync_WhenSubmodelNotInAas_Returns404Async() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string ProductId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{ProductId}/Missing"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns(_ => + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, "Other")], + null) + ]); + + // Act + var response = await _client.GetAsync($"/shell-descriptors/{AasIdentifier}/submodel-descriptors/{encodedSubmodelId}"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasIdAsync_WithDifferentCaseInSubmodelId_ReturnsNotFoundAsync() + { + // Arrange + const string aasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string submodelKey = "Nameplate"; + const string productId = "1170_1160_3052_6568"; + const string submodelTemplateId = "https://admin-shell.io/idta/SubmodelTemplate/DigitalNameplate/3/0"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/nameplate"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns(_ => + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, submodelKey)], + null) + ]); + + _ = _mockSubmodelDescriptorProvider + .GetDataForSubmodelDescriptorByIdAsync(submodelTemplateId, Arg.Any()) + .Returns(new SubmodelDescriptor { Id = requestedSubmodelId, Endpoints = [] }); + + // Act + var response = await _client.GetAsync($"/shell-descriptors/{aasIdentifier}/submodel-descriptors/{encodedSubmodelId}"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + private static string EncodeBase64Url(string plainText) { if (string.IsNullOrWhiteSpace(plainText)) diff --git a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRepository/AasRepositoryControllerTests.cs b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRepository/AasRepositoryControllerTests.cs index 2b9c4cb2..1a425932 100644 --- a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRepository/AasRepositoryControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRepository/AasRepositoryControllerTests.cs @@ -10,6 +10,9 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository; +using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients; using AAS.TwinEngine.DataEngine.ModuleTests.Common; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; @@ -29,6 +32,7 @@ public abstract class AasRepositoryControllerTests : IDisposable private readonly ConfigTestFactory _factory; private readonly ITemplateProvider _mockTemplateProvider; private readonly IFileContentProvider _fileContentProvider; + private readonly ISubmodelRepositoryService _mockSubmodelRepositoryService; private readonly HttpClient _client; private readonly ICreateClient _httpClientFactory; @@ -36,6 +40,7 @@ protected AasRepositoryControllerTests(string configDir) { _mockTemplateProvider = Substitute.For(); _fileContentProvider = Substitute.For(); + _mockSubmodelRepositoryService = Substitute.For(); var mockPluginManifestProvider = Substitute.For(); var mockPluginManifestConflictHandler = Substitute.For(); _httpClientFactory = Substitute.For(); @@ -47,6 +52,7 @@ protected AasRepositoryControllerTests(string configDir) _ = services.AddSingleton(_httpClientFactory); _ = services.AddSingleton(_mockTemplateProvider); _ = services.AddSingleton(_fileContentProvider); + _ = services.AddSingleton(_mockSubmodelRepositoryService); }); _client = _factory.CreateClient(); @@ -631,6 +637,136 @@ public async Task GetThumbnailAsync_ShouldReturn404_WhenThumbnailIsMissing() Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + [Fact] + public async Task GetSubmodelByAasIdAsync_ReturnsOkAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string submodelKey = "Nameplate"; + const string productId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/{submodelKey}"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns([new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, submodelKey)], null)]); + + _ = _mockSubmodelRepositoryService + .GetSubmodelAsync(requestedSubmodelId, Arg.Any(), Arg.Any()) + .Returns(new Submodel(id: requestedSubmodelId)); + + // Act + var response = await _client.GetAsync($"/shells/{AasIdentifier}/submodels/{encodedSubmodelId}"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var json = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(json); + } + + [Fact] + public async Task GetSubmodelByAasIdAsync_WhenSubmodelNotInAas_Returns404Async() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string productId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/Missing"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns([new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, "Other")], null)]); + + // Act + var response = await _client.GetAsync($"/shells/{AasIdentifier}/submodels/{encodedSubmodelId}"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasIdAsync_ReturnsOkAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string submodelKey = "Nameplate"; + const string productId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/{submodelKey}"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns([new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, submodelKey)], null)]); + + _ = _mockSubmodelRepositoryService + .GetAllSubmodelElementsAsync(requestedSubmodelId, Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(new SubmodelElementsPage { PagingMetaData = new PagingMetaData { Cursor = null }, Result = [] }); + + // Act + var response = await _client.GetAsync($"/shells/{AasIdentifier}/submodels/{encodedSubmodelId}/submodel-elements"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var json = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(json); + Assert.True(json.ContainsKey("result")); + } + + [Fact] + public async Task GetSubmodelElementByAasIdAsync_ReturnsOkAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string submodelKey = "Nameplate"; + const string productId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/{submodelKey}"; + const string IdShortPath = "ManufacturerName"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns([new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, submodelKey)], null)]); + + _ = _mockSubmodelRepositoryService + .GetSubmodelElementAsync(requestedSubmodelId, IdShortPath, Arg.Any(), Arg.Any()) + .Returns(new Property(DataTypeDefXsd.String) { IdShort = IdShortPath }); + + // Act + var response = await _client.GetAsync($"/shells/{AasIdentifier}/submodels/{encodedSubmodelId}/submodel-elements/{IdShortPath}"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var json = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(json); + } + + [Fact] + public async Task GetFileByPathByAasIdAsync_ReturnsAttachmentStreamAsync() + { + // Arrange + const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg="; + const string submodelKey = "Nameplate"; + const string productId = "1170_1160_3052_6568"; + var requestedSubmodelId = $"https://mm-software.com/submodel/{productId}/{submodelKey}"; + const string idShortPath = "Thumbnail"; + var encodedSubmodelId = EncodeBase64Url(requestedSubmodelId); + var fileBytes = Encoding.UTF8.GetBytes("fake-image-bytes"); + + _ = _mockTemplateProvider.GetSubmodelRefByIdAsync(Arg.Any(), Arg.Any()) + .Returns([new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, submodelKey)], null)]); + + _ = _mockSubmodelRepositoryService + .GetFileAttachmentAsync(requestedSubmodelId, idShortPath, Arg.Any()) + .Returns(new FileAttachmentResult(new MemoryStream(fileBytes), "image/png", "logo.png", 100 * 1024 * 1024)); + + // Act + var response = await _client.GetAsync($"/shells/{AasIdentifier}/submodels/{encodedSubmodelId}/submodel-elements/{idShortPath}/attachment"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("image/png", response.Content.Headers.ContentType?.MediaType); + var body = await response.Content.ReadAsByteArrayAsync(); + Assert.Equal(fileBytes, body); + Assert.Contains("logo.png", response.Content.Headers.ContentDisposition?.ToString(), StringComparison.Ordinal); + await _mockSubmodelRepositoryService.Received(1).GetFileAttachmentAsync(requestedSubmodelId, idShortPath, Arg.Any()); + } + private static string EncodeBase64Url(string plainText) { if (string.IsNullOrWhiteSpace(plainText)) diff --git a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/GetAllSubmodelElementsControllerTests.cs b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/GetAllSubmodelElementsControllerTests.cs index d7ddc9f4..0ac9ecd6 100644 --- a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/GetAllSubmodelElementsControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/GetAllSubmodelElementsControllerTests.cs @@ -80,7 +80,7 @@ public async Task GetAllSubmodelElementsAsync_WithNoQueryParams_ReturnsOkWithEmp }; _ = _mockSubmodelRepositoryService - .GetAllSubmodelElementsAsync(SubmodelId, null, null, null, Arg.Any()) + .GetAllSubmodelElementsAsync(SubmodelId, Arg.Any(), null, null, Arg.Any()) .Returns(elementList); // Act @@ -105,7 +105,7 @@ public async Task GetAllSubmodelElementsAsync_WithElements_ReturnsPopulatedResul }; _ = _mockSubmodelRepositoryService - .GetAllSubmodelElementsAsync(SubmodelId, null, null, null, Arg.Any()) + .GetAllSubmodelElementsAsync(SubmodelId, Arg.Any(), null, null, Arg.Any()) .Returns(elementList); // Act diff --git a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/SubmodelRepositoryControllerTests.cs b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/SubmodelRepositoryControllerTests.cs index e9966545..ba6e931d 100644 --- a/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/SubmodelRepositoryControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/SubmodelRepositoryControllerTests.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text; using System.Text.Json.Nodes; @@ -9,8 +8,11 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; +using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients; using AAS.TwinEngine.DataEngine.ModuleTests.Common; +using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; @@ -18,10 +20,6 @@ using NSubstitute; using NSubstitute.ExceptionExtensions; -using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; -using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; -using AAS.TwinEngine.DataEngine.DomainModel.Shared; - namespace AAS.TwinEngine.DataEngine.ModuleTests.Api.Services.SubmodelRepository; public abstract class SubmodelRepositoryControllerTests : IDisposable @@ -91,7 +89,7 @@ public async Task GetSubmodelAsync_WithValidIdentifier_ReturnsOkAsync() const string SubmodelId = "Q29udGFjdEluZm9ybWF0aW9u"; var mockSubmodel = TestData.CreateSubmodel(); - _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), null, Arg.Any()).Returns(mockSubmodel); + _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(mockSubmodel); // Act var response = await _client.GetAsync($"/submodels/{SubmodelId}"); @@ -110,7 +108,7 @@ public async Task GetSubmodelAsync_WithNotFound_Returns404Async() { const string SubmodelId = "Q29udGFjdEluZm9ybWF0aW9u"; - _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), null, Arg.Any()).Throws(new ResourceNotFoundException()); + _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new ResourceNotFoundException()); var response = await _client.GetAsync($"/submodels/{SubmodelId}"); @@ -179,7 +177,7 @@ public async Task GetSubmodelElementAsync_WithNotFound_Returns404Async() { const string SubmodelId = "Q29udGFjdEluZm9ybWF0aW9u"; - _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), null, Arg.Any()).Throws(new ResourceNotFoundException()); + _ = _mockTemplateProvider.GetFilteredSubmodelTemplateAsync(Arg.Any(), Arg.Any(), Arg.Any()).Throws(new ResourceNotFoundException()); var response = await _client.GetAsync(CreateSubmodelElementPath(SubmodelId, "Test")); diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/Handler/ShellDescriptorHandlerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/Handler/ShellDescriptorHandlerTests.cs index e5ba7655..eec78680 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/Handler/ShellDescriptorHandlerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/Handler/ShellDescriptorHandlerTests.cs @@ -1,15 +1,24 @@ using AAS.TwinEngine.DataEngine.Api.AasRegistry.Handler; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Responses; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRegistry; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; +using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; +using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry; using AAS.TwinEngine.DataEngine.UnitTests.Api.Shared.MappingProfiles; +using AasCore.Aas3_1; + using Microsoft.Extensions.Logging; using NSubstitute; +using NSubstitute.ExceptionExtensions; namespace AAS.TwinEngine.DataEngine.UnitTests.Api.AasRegistry.Handler; @@ -105,4 +114,80 @@ public async Task GetShellTemplateAsync_InvalidAasIdentifier_ThrowsInvalidUserIn var exception = await Assert.ThrowsAsync(() => _sut.GetShellDescriptorById(request, CancellationToken.None)); Assert.Equal("Invalid User Input.", exception.Message); } + + [Fact] + public async Task GetSubmodelDescriptorByAasId_ReturnsDescriptor_WhenSubmodelBelongsToAas() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelDescriptorByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url()); + _shellDescriptorService.GetSubmodelDescriptorByAasIdAsync(AasId, SubmodelId, Arg.Any()) + .Returns(new SubmodelDescriptor { Id = SubmodelId }); + + var result = await _sut.GetSubmodelDescriptorByAasId(request, CancellationToken.None); + + Assert.IsType(result); + await _shellDescriptorService.Received(1) + .GetSubmodelDescriptorByAasIdAsync(AasId, SubmodelId, Arg.Any()); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasId_SubmodelNotInAas_ThrowsSubmodelDescriptorNotFoundException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelDescriptorByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url()); + _shellDescriptorService.GetSubmodelDescriptorByAasIdAsync(AasId, SubmodelId, Arg.Any()) + .ThrowsAsync(new SubmodelDescriptorNotFoundException()); + + await Assert.ThrowsAsync( + () => _sut.GetSubmodelDescriptorByAasId(request, CancellationToken.None)); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasId_AasNotFound_PropagatesException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelDescriptorByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url()); + _shellDescriptorService.GetSubmodelDescriptorByAasIdAsync(AasId, SubmodelId, Arg.Any()) + .ThrowsAsync(new TemplateNotFoundException()); + + await Assert.ThrowsAsync( + () => _sut.GetSubmodelDescriptorByAasId(request, CancellationToken.None)); + } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasId_ReturnsDescriptors_ForAllSubmodelsInAas() + { + const string AasId = "AasId"; + var request = new GetSubmodelDescriptorsByAasRequest(AasId.EncodeBase64Url(), null, null); + _shellDescriptorService.GetAllSubmodelDescriptorsByAasIdAsync(AasId, null, null, Arg.Any()) + .Returns(new SubmodelDescriptors + { + PagingMetaData = new PagingMetaData(), + Result = + [ + new SubmodelDescriptor { Id = "SubmodelId1" }, + new SubmodelDescriptor { Id = "SubmodelId2" } + ] + }); + + var result = await _sut.GetAllSubmodelDescriptorsByAasId(request, CancellationToken.None); + + Assert.NotNull(result); + Assert.Equal(2, result.Result?.Count); + } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasId_AasNotFound_PropagatesException() + { + const string AasId = "AasId"; + var request = new GetSubmodelDescriptorsByAasRequest(AasId.EncodeBase64Url(), null, null); + _shellDescriptorService.GetAllSubmodelDescriptorsByAasIdAsync(AasId, null, null, Arg.Any()) + .ThrowsAsync(new TemplateNotFoundException()); + + await Assert.ThrowsAsync( + () => _sut.GetAllSubmodelDescriptorsByAasId(request, CancellationToken.None)); + } } diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/ShellDescriptorControllerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/ShellDescriptorControllerTests.cs index 14ca8dbd..40d282fb 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/ShellDescriptorControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRegistry/ShellDescriptorControllerTests.cs @@ -3,6 +3,7 @@ using AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Responses; using AAS.TwinEngine.DataEngine.Api.Shared; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Base; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; @@ -176,4 +177,41 @@ private static ShellDescriptorDto CreateShellDescriptor(string idShort, string i SubmodelDescriptors = null! }; } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasIdAsync_ReturnsOkResult() + { + const string AasId = "https://example.com/ids/aas/1170_1160_3052_6568"; + var encodedId = AasId.EncodeBase64Url(); + var expected = new SubmodelDescriptorsDto { PagingMetaData = new PagingMetaDataDto(), Result = [] }; + _handler.GetAllSubmodelDescriptorsByAasId(Arg.Any(), Arg.Any()) + .Returns(expected); + + var result = await _sut.GetAllSubmodelDescriptorsByAasIdAsync(encodedId, null, null, CancellationToken.None); + + var okResult = Assert.IsType(result.Result); + Assert.IsType(okResult.Value); + await _handler.Received(1).GetAllSubmodelDescriptorsByAasId( + Arg.Is(r => r.AasIdentifier == encodedId), + Arg.Any()); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasIdAsync_ReturnsOkResult() + { + const string AasId = "https://example.com/ids/aas/1170_1160_3052_6568"; + const string SubmodelId = "https://example.com/submodel/1"; + var encodedAasId = AasId.EncodeBase64Url(); + var encodedSubmodelId = SubmodelId.EncodeBase64Url(); + _handler.GetSubmodelDescriptorByAasId(Arg.Any(), Arg.Any()) + .Returns(_expectedShellDescriptor.SubmodelDescriptors?.FirstOrDefault() ?? new SubmodelDescriptorDto()); + + var result = await _sut.GetSubmodelDescriptorByAasIdAsync(encodedAasId, encodedSubmodelId, CancellationToken.None); + + var okResult = Assert.IsType(result.Result); + Assert.NotNull(okResult.Value); + await _handler.Received(1).GetSubmodelDescriptorByAasId( + Arg.Is(r => r.AasIdentifier == encodedAasId && r.SubmodelIdentifier == encodedSubmodelId), + Arg.Any()); + } } diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/AasRepositoryControllerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/AasRepositoryControllerTests.cs index 04950440..f4e7b0dc 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/AasRepositoryControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/AasRepositoryControllerTests.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -8,6 +8,7 @@ using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses; using AAS.TwinEngine.DataEngine.Api.Shared; using AAS.TwinEngine.DataEngine.Api.Shared.Results; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.DomainModel.Shared; @@ -56,7 +57,8 @@ public AasRepositoryControllerTests() public async Task GetShellsByAssetIdAsync_ReturnsOkResult() { var expectedResponse = new ShellsDto { PagingMetaData = new PagingMetaDataDto { Cursor = null }, Result = [] }; - _handler.GetShellsByAssetIdsAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + var request = new GetShellsByAssetIdsRequest(["dGVzdA"], null, null, null); + _handler.GetShellsByAssetIdsAsync(request, Arg.Any()) .Returns(expectedResponse); var result = await _sut.GetShellsByAssetIdAsync(["dGVzdA"], null, null, null, CancellationToken.None); @@ -69,19 +71,21 @@ public async Task GetShellsByAssetIdAsync_WithIdShort_ReturnsOkResult() { var expectedResponse = new ShellsDto { PagingMetaData = new PagingMetaDataDto { Cursor = null }, Result = [] }; const string idShort = "test-idshort"; - _handler.GetShellsByAssetIdsAsync(Arg.Any(), idShort, Arg.Any(), Arg.Any(), Arg.Any()) + _handler.GetShellsByAssetIdsAsync(Arg.Any(), Arg.Any()) .Returns(expectedResponse); var result = await _sut.GetShellsByAssetIdAsync(["dGVzdA"], idShort, null, null, CancellationToken.None); Assert.IsType>(result); - await _handler.Received(1).GetShellsByAssetIdsAsync(Arg.Any(), idShort, Arg.Any(), Arg.Any(), Arg.Any()); + await _handler.Received(1).GetShellsByAssetIdsAsync( + Arg.Is(r => r.IdShort == idShort), + Arg.Any()); } [Fact] public async Task GetShellsByAssetIdAsync_ThrowsException_Propagates() { - _handler.GetShellsByAssetIdsAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + _handler.GetShellsByAssetIdsAsync(Arg.Any(), Arg.Any()) .Throws(new Exception("error")); var exception = await Record.ExceptionAsync(() => _sut.GetShellsByAssetIdAsync(["dGVzdA"], null, null, null, CancellationToken.None)); @@ -288,6 +292,82 @@ public async Task GetThumbnailAsync_ShouldReturnFileContentStreamResult_WhenHand Assert.IsType(response); } + [Fact] + public async Task GetSubmodelByAasIdAsync_ReturnsOkWithJson() + { + var encodedAasId = AasIdentifier.EncodeBase64Url(); + const string SubmodelId = "SubmodelId"; + var encodedSubmodelId = SubmodelId.EncodeBase64Url(); + var submodel = new Submodel(SubmodelId); + _handler.GetSubmodelByAasIdAsync(Arg.Any(), Arg.Any()) + .Returns(submodel); + + var result = await _sut.GetSubmodelByAasIdAsync(encodedAasId, encodedSubmodelId, CancellationToken.None); + + var okResult = Assert.IsType(result.Result); + Assert.IsType(okResult.Value); + await _handler.Received(1).GetSubmodelByAasIdAsync( + Arg.Is(r => r.AasIdentifier == encodedAasId && r.SubmodelId == encodedSubmodelId), + Arg.Any()); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasIdAsync_ReturnsOk() + { + var encodedAasId = AasIdentifier.EncodeBase64Url(); + const string SubmodelId = "SubmodelId"; + var encodedSubmodelId = SubmodelId.EncodeBase64Url(); + var expected = new SubmodelElementsDto { PagingMetaData = new PagingMetaDataDto(), Result = [] }; + _handler.GetAllSubmodelElementsByAasIdAsync(Arg.Any(), Arg.Any()) + .Returns(expected); + + var result = await _sut.GetAllSubmodelElementsByAasIdAsync(encodedAasId, encodedSubmodelId, null, null, CancellationToken.None); + + var okResult = Assert.IsType(result.Result); + Assert.IsType(okResult.Value); + await _handler.Received(1).GetAllSubmodelElementsByAasIdAsync( + Arg.Is(r => r.AasIdentifier == encodedAasId && r.SubmodelId == encodedSubmodelId), + Arg.Any()); + } + + [Fact] + public async Task GetSubmodelElementByAasIdAsync_ReturnsOkWithJson() + { + var encodedAasId = AasIdentifier.EncodeBase64Url(); + const string SubmodelId = "SubmodelId"; + var encodedSubmodelId = SubmodelId.EncodeBase64Url(); + const string IdShortPath = "ManufacturerName"; + var element = new Property(idShort: IdShortPath, valueType: DataTypeDefXsd.String); + _handler.GetSubmodelElementByAasIdAsync(Arg.Any(), Arg.Any()) + .Returns(element); + + var result = await _sut.GetSubmodelElementByAasIdAsync(encodedAasId, encodedSubmodelId, IdShortPath, CancellationToken.None); + + var okResult = Assert.IsType(result.Result); + Assert.IsType(okResult.Value); + await _handler.Received(1).GetSubmodelElementByAasIdAsync( + Arg.Is(r => r.AasIdentifier == encodedAasId && r.SubmodelId == encodedSubmodelId && r.IdShortPath == IdShortPath), + Arg.Any()); + } + + [Fact] + public async Task GetFileByPathByAasIdAsync_ReturnsFileContentStreamResult() + { + var encodedAasId = AasIdentifier.EncodeBase64Url(); + const string SubmodelId = "SubmodelId"; + var encodedSubmodelId = SubmodelId.EncodeBase64Url(); + const string IdShortPath = "Thumbnail"; + + _handler.GetFileByPathByAasIdAsync(Arg.Any(), Arg.Any()) + .Returns(new FileAttachmentResult(Stream.Null, "image/png", "logo.png", 100 * 1024 * 1024)); + + var result = await _sut.GetFileByPathByAasIdAsync(encodedAasId, encodedSubmodelId, IdShortPath, CancellationToken.None); + + Assert.IsType(result); + await _handler.Received(1).GetFileByPathByAasIdAsync( + Arg.Is(r => r.AasIdentifier == encodedAasId && r.SubmodelIdentifier == encodedSubmodelId && r.IdShortPath == IdShortPath), + Arg.Any()); + } [Fact] public async Task GetThumbnailAsync_PassesRouteValuesToHandler() diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/Handler/AasRepositoryHandlerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/Handler/AasRepositoryHandlerTests.cs index 7ab7a138..7d0a190b 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/Handler/AasRepositoryHandlerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/Handler/AasRepositoryHandlerTests.cs @@ -1,19 +1,21 @@ -using AAS.TwinEngine.DataEngine.Api.AasRepository.Handler; +using AAS.TwinEngine.DataEngine.Api.AasRepository.Handler; using AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses; using AAS.TwinEngine.DataEngine.Api.Shared; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AasCore.Aas3_1; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using NSubstitute; +using NSubstitute.ExceptionExtensions; namespace AAS.TwinEngine.DataEngine.UnitTests.Api.AasRepository.Handler; @@ -30,8 +32,9 @@ public async Task GetShellsByAssetIdsAsync_WithNullAssetIds_ReturnsAllShells() { _ = _aasRepositoryService.GetShellsByFiltersAsync(Arg.Any(), null, null, Arg.Any()) .Returns(new Shells { PagingMetaData = new PagingMetaData { Cursor = null }, Result = [] }); + var request = new GetShellsByAssetIdsRequest(null, null, null, null); - var result = await _sut.GetShellsByAssetIdsAsync(null, null, null, null, CancellationToken.None); + var result = await _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None); Assert.NotNull(result); await _aasRepositoryService.Received().GetShellsByFiltersAsync(Arg.Is(f => f.SpecificAssetIds == null && f.IdShort == null), null, null, Arg.Any()); @@ -42,8 +45,9 @@ public async Task GetShellsByAssetIdsAsync_WithEmptyAssetIds_ReturnsAllShells() { _ = _aasRepositoryService.GetShellsByFiltersAsync(Arg.Any(), null, null, Arg.Any()) .Returns(new Shells { PagingMetaData = new PagingMetaData { Cursor = null }, Result = [] }); + var request = new GetShellsByAssetIdsRequest([], null, null, null); - var result = await _sut.GetShellsByAssetIdsAsync([], null, null, null, CancellationToken.None); + var result = await _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None); Assert.NotNull(result); await _aasRepositoryService.Received().GetShellsByFiltersAsync(Arg.Is(f => f.SpecificAssetIds == null && f.IdShort == null), null, null, Arg.Any()); @@ -53,9 +57,10 @@ public async Task GetShellsByAssetIdsAsync_WithEmptyAssetIds_ReturnsAllShells() public async Task GetShellsByAssetIdsAsync_WithInvalidBase64Url_ThrowsInvalidUserInputException() { var assetIds = new[] { "not-valid-base64!!!" }; + var request = new GetShellsByAssetIdsRequest(assetIds, null, null, null); await Assert.ThrowsAsync( - () => _sut.GetShellsByAssetIdsAsync(assetIds, null, null, null, CancellationToken.None)); + () => _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None)); } [Fact] @@ -65,7 +70,7 @@ public async Task GetShellsByAssetIdsAsync_WithValidInput_ReturnsShells() var encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(json)) .Replace('+', '-').Replace('/', '_').TrimEnd('='); var assetIds = new[] { encoded }; - + var request = new GetShellsByAssetIdsRequest(assetIds, null, null, null); var shell = new AssetAdministrationShell( "urn:example:aas:001", new AssetInformation(AssetKind.Instance)); @@ -74,7 +79,7 @@ public async Task GetShellsByAssetIdsAsync_WithValidInput_ReturnsShells() Arg.Any(), null, null, Arg.Any()) .Returns(new Shells { PagingMetaData = new PagingMetaData { Cursor = null }, Result = [shell] }); - var result = await _sut.GetShellsByAssetIdsAsync(assetIds, null, null, null, CancellationToken.None); + var result = await _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None); Assert.NotNull(result); Assert.Single(result.Result!); @@ -88,8 +93,10 @@ public async Task GetShellsByAssetIdsAsync_WithNegativeLimit_ThrowsInvalidUserIn .Replace('+', '-').Replace('/', '_').TrimEnd('='); var assetIds = new[] { encoded }; + var request = new GetShellsByAssetIdsRequest(assetIds, null, -1, null); + await Assert.ThrowsAsync( - () => _sut.GetShellsByAssetIdsAsync(assetIds, null, -1, null, CancellationToken.None)); + () => _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None)); } [Fact] @@ -98,8 +105,9 @@ public async Task GetShellsByAssetIdsAsync_WithIdShort_PassesIdShortToService() const string idShort = "test-idshort"; _ = _aasRepositoryService.GetShellsByFiltersAsync(Arg.Any(), null, null, Arg.Any()) .Returns(new Shells { PagingMetaData = new PagingMetaData { Cursor = null }, Result = [] }); + var request = new GetShellsByAssetIdsRequest(null, idShort, null, null); - var result = await _sut.GetShellsByAssetIdsAsync(null, idShort, null, null, CancellationToken.None); + var result = await _sut.GetShellsByAssetIdsAsync(request, CancellationToken.None); Assert.NotNull(result); await _aasRepositoryService.Received().GetShellsByFiltersAsync(Arg.Is(f => f.SpecificAssetIds == null && f.IdShort == idShort), null, null, Arg.Any()); @@ -329,5 +337,246 @@ private static AssetInformation CreateAssetInformation() defaultThumbnail: thumbnail ); } + + [Fact] + public async Task GetSubmodelByAasIdAsync_ReturnsSubmodel_WhenSubmodelBelongsToAas() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url(), Level.deep, Extent.withoutBlobValue); + var expectedSubmodel = new Submodel(SubmodelId); + _aasRepositoryService.GetSubmodelByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()) + .Returns(expectedSubmodel); + + var result = await _sut.GetSubmodelByAasIdAsync(request, CancellationToken.None); + + Assert.IsType(result); + await _aasRepositoryService.Received(1) + .GetSubmodelByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()); + } + + [Fact] + public async Task GetSubmodelByAasIdAsync_SubmodelNotInAas_ThrowsSubmodelNotFoundException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url(), Level.deep, Extent.withoutBlobValue); + _aasRepositoryService.GetSubmodelByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()) + .ThrowsAsync(new SubmodelNotFoundException(SubmodelId)); + + await Assert.ThrowsAsync( + () => _sut.GetSubmodelByAasIdAsync(request, CancellationToken.None)); + } + + [Fact] + public async Task GetSubmodelByAasIdAsync_AasNotFound_PropagatesException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetSubmodelByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url(), Level.deep, Extent.withoutBlobValue); + _aasRepositoryService.GetSubmodelByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()) + .ThrowsAsync(new TemplateNotFoundException()); + + await Assert.ThrowsAsync( + () => _sut.GetSubmodelByAasIdAsync(request, CancellationToken.None)); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasIdAsync_ReturnsList_WhenSubmodelBelongsToAas() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetAllSubmodelElementsByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url(), null, null, Level.deep, Extent.withoutBlobValue); + _aasRepositoryService.GetAllSubmodelElementsByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + null, + null, + Arg.Any()) + .Returns(new SubmodelElementsPage { PagingMetaData = new PagingMetaData(), Result = [] }); + + var result = await _sut.GetAllSubmodelElementsByAasIdAsync(request, CancellationToken.None); + + Assert.NotNull(result); + await _aasRepositoryService.Received(1) + .GetAllSubmodelElementsByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + null, + null, + Arg.Any()); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasIdAsync_SubmodelNotInAas_ThrowsSubmodelNotFoundException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + var request = new GetAllSubmodelElementsByAasRequest(AasId.EncodeBase64Url(), SubmodelId.EncodeBase64Url(), null, null, Level.deep, Extent.withoutBlobValue); + _aasRepositoryService.GetAllSubmodelElementsByAasIdAsync( + AasId, + SubmodelId, + Arg.Is(o => o.Level == Level.deep.ToString() && o.Extent == Extent.withoutBlobValue.ToString()), + null, + null, + Arg.Any()) + .ThrowsAsync(new SubmodelNotFoundException(SubmodelId)); + + await Assert.ThrowsAsync( + () => _sut.GetAllSubmodelElementsByAasIdAsync(request, CancellationToken.None)); + } + + [Fact] + public async Task GetSubmodelElementByAasIdAsync_ReturnsElement_WhenSubmodelBelongsToAas() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + const string IdShortPath = "ManufacturerName"; + var request = new GetSubmodelElementByAasRequest( + AasId.EncodeBase64Url(), + SubmodelId.EncodeBase64Url(), + IdShortPath, + Level.deep, + Extent.withoutBlobValue); + var expectedElement = new Property(idShort: IdShortPath, valueType: DataTypeDefXsd.String); + _aasRepositoryService.GetSubmodelElementByAasIdAsync(AasId, SubmodelId, IdShortPath, Arg.Any(), Arg.Any()) + .Returns(expectedElement); + + var result = await _sut.GetSubmodelElementByAasIdAsync(request, CancellationToken.None); + + Assert.IsType(result); + await _aasRepositoryService.Received(1) + .GetSubmodelElementByAasIdAsync(AasId, SubmodelId, IdShortPath, Arg.Any(), Arg.Any()); + } + + [Fact] + public async Task GetSubmodelElementByAasIdAsync_SubmodelNotInAas_ThrowsSubmodelNotFoundException() + { + const string AasId = "AasId"; + const string SubmodelId = "SubmodelId"; + const string idShortPath = "SomePath"; + var request = new GetSubmodelElementByAasRequest( + AasId.EncodeBase64Url(), + SubmodelId.EncodeBase64Url(), + idShortPath, + Level.deep, + Extent.withoutBlobValue); + _aasRepositoryService.GetSubmodelElementByAasIdAsync(AasId, SubmodelId, idShortPath, Arg.Any(), Arg.Any()) + .ThrowsAsync(new SubmodelNotFoundException(SubmodelId)); + + await Assert.ThrowsAsync( + () => _sut.GetSubmodelElementByAasIdAsync(request, CancellationToken.None)); + } + + [Fact] + public async Task GetSubmodelByAasIdAsync_WithCoreAndWithBlobValue_PassesQueryOptionsToService() + { + const string aasId = "AasId"; + const string submodelId = "SubmodelId"; + var request = new GetSubmodelByAasRequest(aasId.EncodeBase64Url(), submodelId.EncodeBase64Url(), Level.core, Extent.withBlobValue); + var expectedSubmodel = new Submodel(submodelId); + + _aasRepositoryService.GetSubmodelByAasIdAsync( + aasId, + submodelId, + Arg.Is(o => o.Level == Level.core.ToString() && o.Extent == Extent.withBlobValue.ToString()), + Arg.Any()) + .Returns(expectedSubmodel); + + var result = await _sut.GetSubmodelByAasIdAsync(request, CancellationToken.None); + + Assert.Same(expectedSubmodel, result); + await _aasRepositoryService.Received(1).GetSubmodelByAasIdAsync( + aasId, + submodelId, + Arg.Is(o => o.Level == Level.core.ToString() && o.Extent == Extent.withBlobValue.ToString()), + Arg.Any()); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + public async Task GetSubmodelElementByAasIdAsync_InvalidIdShortPath_ThrowsInvalidUserInputException(string idShortPath) + { + const string aasId = "AasId"; + const string submodelId = "SubmodelId"; + var request = new GetSubmodelElementByAasRequest( + aasId.EncodeBase64Url(), + submodelId.EncodeBase64Url(), + idShortPath, + Level.deep, + Extent.withoutBlobValue); + + await Assert.ThrowsAsync(() => + _sut.GetSubmodelElementByAasIdAsync(request, CancellationToken.None)); + } + + [Fact] + public async Task GetFileByPathByAasIdAsync_ReturnsAttachment_WhenInputIsValid() + { + const string aasId = "https://example.com/aas/1"; + const string submodelId = "https://example.com/submodels/contact"; + const string idShortPath = "Thumbnail"; + + var request = new GetFileByPathByAasIdRequest( + aasId.EncodeBase64Url(), + submodelId.EncodeBase64Url(), + idShortPath); + + var expected = new FileAttachmentResult(Stream.Null, "image/png", "thumbnail.png", 100 * 1024 * 1024); + + _aasRepositoryService.GetFileAttachmentByAasIdAsync( + aasId, + submodelId, + idShortPath, + Arg.Any()) + .Returns(expected); + + var result = await _sut.GetFileByPathByAasIdAsync(request, CancellationToken.None); + + Assert.Same(expected, result); + await _aasRepositoryService.Received(1).GetFileAttachmentByAasIdAsync( + aasId, + submodelId, + idShortPath, + Arg.Any()); + } + + [Theory] + [InlineData("../../../etc/passwd")] + [InlineData(@"..\\..\\windows\\system32")] + [InlineData("element/../otherElement")] + [InlineData("%2e%2e/config")] + public async Task GetFileByPathByAasIdAsync_InvalidIdShortPath_ThrowsInvalidUserInputException(string maliciousPath) + { + const string aasId = "https://example.com/aas/1"; + const string submodelId = "https://example.com/submodels/contact"; + + var request = new GetFileByPathByAasIdRequest( + aasId.EncodeBase64Url(), + submodelId.EncodeBase64Url(), + maliciousPath); + + await Assert.ThrowsAsync(() => + _sut.GetFileByPathByAasIdAsync(request, CancellationToken.None)); + } } diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/Handler/SubmodelRepositoryHandlerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/Handler/SubmodelRepositoryHandlerTests.cs index 76ddad78..83b2b68b 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/Handler/SubmodelRepositoryHandlerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/Handler/SubmodelRepositoryHandlerTests.cs @@ -49,7 +49,7 @@ public async Task HandleSubmodel_WithLevelAndExtent_PassesQueryOptionsToService( { const string SubmodelId = "NameplateSubmodel"; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); - var request = new GetSubmodelRequest(encodedId) { Level = Level.deep, Extent = Extent.withBlobValue }; + var request = new GetSubmodelRequest(encodedId, Level.deep, Extent.withBlobValue); var expectedSubmodel = Substitute.For(); _submodelRepository .GetSubmodelAsync(SubmodelId, Arg.Is(q => q != null), Arg.Any()) @@ -62,19 +62,29 @@ await _submodelRepository.Received(1) } [Fact] - public async Task HandleSubmodel_WithNoLevelOrExtent_PassesNullQueryOptionsToService() + public async Task HandleSubmodel_WithDefaultLevelAndExtent_PassesQueryOptionsToService() { const string SubmodelId = "NameplateSubmodel"; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); var request = new GetSubmodelRequest(encodedId); _submodelRepository - .GetSubmodelAsync(SubmodelId, (SubmodelQueryOptions?)null, Arg.Any()) + .GetSubmodelAsync( + SubmodelId, + Arg.Is(q => q != null + && q.Level == Level.deep.ToString() + && q.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()) .Returns(Substitute.For()); await _sut.GetSubmodel(request, CancellationToken.None); await _submodelRepository.Received(1) - .GetSubmodelAsync(SubmodelId, (SubmodelQueryOptions?)null, Arg.Any()); + .GetSubmodelAsync( + SubmodelId, + Arg.Is(q => q != null + && q.Level == Level.deep.ToString() + && q.Extent == Extent.withoutBlobValue.ToString()), + Arg.Any()); } [Fact] @@ -107,12 +117,12 @@ public async Task HandleSubmodelElement_ReturnsSubmodel_WhenSubmodelElementExist var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); var request = new GetSubmodelElementRequest(encodedId, IdShortPath); var submodelElement = Substitute.For(); - _submodelRepository.GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Any()).Returns(submodelElement); + _submodelRepository.GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Any(), Arg.Any()).Returns(submodelElement); var result = await _sut.GetSubmodelElement(request, CancellationToken.None); Assert.Equal(submodelElement, result); - await _submodelRepository.Received().GetSubmodelElementAsync(Arg.Any(), Arg.Any(), Arg.Any()); + await _submodelRepository.Received().GetSubmodelElementAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()); } [Fact] @@ -122,11 +132,29 @@ public async Task HandleSubmodelElement_SubmodelIsNull_ThrowsSubmodelNotFoundExc var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); const string IdShortPath = "Segments.LinkedSegment"; var request = new GetSubmodelElementRequest(encodedId, IdShortPath); - _submodelRepository.GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Any()).Returns((ISubmodelElement)null!); + _submodelRepository.GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Any(), Arg.Any()).Returns((ISubmodelElement)null!); await Assert.ThrowsAsync(() => _sut.GetSubmodelElement(request, CancellationToken.None)); } + [Fact] + public async Task HandleSubmodelElement_WithLevelAndExtent_PassesQueryOptionsToService() + { + const string SubmodelId = "NameplateSubmodel"; + const string IdShortPath = "Segments.LinkedSegment"; + var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); + var request = new GetSubmodelElementRequest(encodedId, IdShortPath, Level.deep, Extent.withBlobValue); + var submodelElement = Substitute.For(); + _submodelRepository + .GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Is(q => q != null), Arg.Any()) + .Returns(submodelElement); + + await _sut.GetSubmodelElement(request, CancellationToken.None); + + await _submodelRepository.Received(1) + .GetSubmodelElementAsync(SubmodelId, IdShortPath, Arg.Is(q => q != null), Arg.Any()); + } + [Fact] public async Task HandleSubmodelElement_InvalidBase64SubmodelId_ThrowsInternalDataProcessingException() { @@ -255,7 +283,7 @@ public async Task HandleSubmodelElement_ValidIdShortPath_DoesNotThrow(string val var encodedId = SubmodelId.EncodeBase64Url(); var request = new GetSubmodelElementRequest(encodedId, validIdShortPath); var submodelElement = Substitute.For(); - _submodelRepository.GetSubmodelElementAsync(SubmodelId, validIdShortPath, Arg.Any()).Returns(submodelElement); + _submodelRepository.GetSubmodelElementAsync(SubmodelId, validIdShortPath, Arg.Any(), Arg.Any()).Returns(submodelElement); var result = await _sut.GetSubmodelElement(request, CancellationToken.None); @@ -363,7 +391,7 @@ public async Task GetAllSubmodelElements_DecodesSubmodelIdAndPassesToService() { const string SubmodelId = "NameplateSubmodel"; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); - var request = new GetAllSubmodelElementsRequest(encodedId, null, null); + var request = new GetAllSubmodelElementsRequest(encodedId, null, null, Level.deep, Extent.withoutBlobValue); var elementList = new SubmodelElementsPage { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] }; _submodelRepository @@ -384,7 +412,7 @@ public async Task GetAllSubmodelElements_PassesLimitAndCursorToService() const int Limit = 5; const string Cursor = "dGVzdA=="; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); - var request = new GetAllSubmodelElementsRequest(encodedId, Limit, Cursor); + var request = new GetAllSubmodelElementsRequest(encodedId, Limit, Cursor, Level.deep, Extent.withoutBlobValue); var elementList = new SubmodelElementsPage { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] }; _submodelRepository @@ -402,7 +430,7 @@ public async Task GetAllSubmodelElements_ReturnsDto_WithElementsAndPagingMetadat { const string SubmodelId = "NameplateSubmodel"; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); - var request = new GetAllSubmodelElementsRequest(encodedId, null, null); + var request = new GetAllSubmodelElementsRequest(encodedId, null, null, Level.deep, Extent.withoutBlobValue); var element = Substitute.For(); element.IdShort.Returns("ManufacturerName"); var elementList = new SubmodelElementsPage @@ -440,21 +468,35 @@ await _submodelRepository.Received(1) } [Fact] - public async Task GetAllSubmodelElements_WithNoLevelOrExtent_PassesNullQueryOptionsToService() + public async Task GetAllSubmodelElements_WithNoLevelOrExtent_PassesDefaultQueryOptionsToService() { const string SubmodelId = "NameplateSubmodel"; var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(SubmodelId)); - var request = new GetAllSubmodelElementsRequest(encodedId, null, null); - var elementList = new SubmodelElementsPage { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] }; + var request = new GetAllSubmodelElementsRequest(encodedId, null, null, Level.deep, Extent.withoutBlobValue); + var elementList = new SubmodelElementsPage { PagingMetaData = new PagingMetaData(), Result = [] }; _submodelRepository - .GetAllSubmodelElementsAsync(SubmodelId, null, null, null, Arg.Any()) + .GetAllSubmodelElementsAsync( + SubmodelId, + Arg.Is(q => q != null + && q.Level == Level.deep.ToString() + && q.Extent == Extent.withoutBlobValue.ToString()), + null, + null, + Arg.Any()) .Returns(elementList); await _sut.GetAllSubmodelElements(request, CancellationToken.None); await _submodelRepository.Received(1) - .GetAllSubmodelElementsAsync(SubmodelId, null, null, null, Arg.Any()); + .GetAllSubmodelElementsAsync( + SubmodelId, + Arg.Is(q => q != null + && q.Level == Level.deep.ToString() + && q.Extent == Extent.withoutBlobValue.ToString()), + null, + null, + Arg.Any()); } [Fact] diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/SubmodelRepositoryControllerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/SubmodelRepositoryControllerTests.cs index 89c78fef..0f3152a3 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/SubmodelRepositoryControllerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/Api/SubmodelRepository/SubmodelRepositoryControllerTests.cs @@ -63,7 +63,7 @@ public async Task GetSubmodelAsync_ReturnsOkResult_WithJsonObject() _handler.GetSubmodel(Arg.Any(), Arg.Any()) .Returns(_expectedSubmodel); - var result = await _sut.GetSubmodelAsync(encodedId, null, null, CancellationToken.None); + var result = await _sut.GetSubmodelAsync(encodedId, CancellationToken.None); var okResult = Assert.IsType(result.Result); var json = Assert.IsType(okResult.Value); @@ -77,12 +77,11 @@ public async Task GetSubmodelAsync_WithLevelAndExtent_PassesThemToHandler() _handler.GetSubmodel(Arg.Any(), Arg.Any()) .Returns(_expectedSubmodel); - await _sut.GetSubmodelAsync(encodedId, AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests.Level.deep, - AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests.Extent.withBlobValue, CancellationToken.None); + await _sut.GetSubmodelAsync(encodedId, CancellationToken.None, Level.deep, Extent.withBlobValue); await _handler.Received(1).GetSubmodel( - Arg.Is(r => r.Level == AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests.Level.deep - && r.Extent == AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests.Extent.withBlobValue), + Arg.Is(r => r.Level == Level.deep + && r.Extent == Extent.withBlobValue), Arg.Any()); } @@ -94,13 +93,27 @@ public async Task GetSubmodelElementAsync_ReturnsOkResult_WithJsonObject() _handler.GetSubmodelElement(Arg.Any(), Arg.Any()) .Returns(_expectedElement); - var result = await _sut.GetSubmodelElementAsync(encodedId, _idShortPath, CancellationToken.None); + var result = await _sut.GetSubmodelElementAsync(encodedId, _idShortPath, CancellationToken.None, Level.deep, Extent.withBlobValue); var okResult = Assert.IsType(result.Result); var json = Assert.IsType(okResult.Value); Assert.Equal(expectedJson.ToJsonString(), json.ToJsonString()); } + [Fact] + public async Task GetSubmodelElementAsync_WithLevelAndExtent_PassesThemToHandler() + { + var encodedId = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(_submodelId)); + _handler.GetSubmodelElement(Arg.Any(), Arg.Any()) + .Returns(_expectedElement); + + await _sut.GetSubmodelElementAsync(encodedId, _idShortPath, CancellationToken.None, Level.deep, Extent.withBlobValue); + + await _handler.Received(1).GetSubmodelElement( + Arg.Is(r => r.Level == Level.deep && r.Extent == Extent.withBlobValue), + Arg.Any()); + } + [Fact] public async Task GetAllSubmodelsAsync_ReturnsOkResult_WithSubmodelsDto() { @@ -112,7 +125,7 @@ public async Task GetAllSubmodelsAsync_ReturnsOkResult_WithSubmodelsDto() _handler.GetAllSubmodels(Arg.Any(), Arg.Any()) .Returns(expectedDto); - var result = await _sut.GetAllSubmodelsAsync(null, null, null, null, null, null, CancellationToken.None); + var result = await _sut.GetAllSubmodelsAsync(null, null, null, null, CancellationToken.None); var okResult = Assert.IsType(result.Result); var dto = Assert.IsType(okResult.Value); @@ -128,9 +141,14 @@ public async Task GetAllSubmodelsAsync_PassesQueryParamsToHandler() var expectedDto = new SubmodelsDto { PagingMetaData = new AAS.TwinEngine.DataEngine.Api.Shared.PagingMetaDataDto(), Result = [] }; _handler.GetAllSubmodels(Arg.Any(), Arg.Any()) .Returns(expectedDto); - var request = new GetAllSubmodelsRequest { SemanticId = SemanticId, IdShort = IdShort, Limit = Limit }; + var request = new GetAllSubmodelsRequest + { + SemanticId = SemanticId, + IdShort = IdShort, + Limit = Limit + }; - await _sut.GetAllSubmodelsAsync(SemanticId, IdShort, Limit, null, null, null, CancellationToken.None); + await _sut.GetAllSubmodelsAsync(SemanticId, IdShort, Limit, null, CancellationToken.None); await _handler.Received(1).GetAllSubmodels( Arg.Is(r => r.SemanticId == SemanticId && r.IdShort == IdShort && r.Limit == Limit), diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRegistry/ShellDescriptorServiceTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRegistry/ShellDescriptorServiceTests.cs index 182e36b8..02c4c770 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRegistry/ShellDescriptorServiceTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRegistry/ShellDescriptorServiceTests.cs @@ -2,10 +2,14 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasEnvironment.Providers; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRegistry; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; +using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Plugin; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; using AasCore.Aas3_1; @@ -25,6 +29,8 @@ public class ShellDescriptorServiceTests private readonly IPluginDataHandler _pluginDataHandler = Substitute.For(); private readonly IShellDescriptorDataHandler _dataHandler = Substitute.For(); private readonly IPluginManifestConflictHandler _pluginManifestConflictHandler = Substitute.For(); + private readonly ISubmodelDescriptorService _submodelDescriptorService = Substitute.For(); + private readonly IAasRepositoryService _aasRepositoryService = Substitute.For(); private readonly ILogger _logger = Substitute.For>(); private readonly IOptions _templateManagementConfig; private readonly ShellDescriptorService _sut; @@ -36,7 +42,68 @@ public ShellDescriptorServiceTests() AasTemplateRegistry = new ServiceInstance { ConcurrentOperationsLimit = 10 } }; _templateManagementConfig = Options.Create(config); - _sut = new ShellDescriptorService(_templateProvider, _shellTemplateMappingProvider, _dataHandler, _pluginDataHandler, _pluginManifestConflictHandler, _logger, _templateManagementConfig); + _sut = new ShellDescriptorService( + _templateProvider, + _shellTemplateMappingProvider, + _dataHandler, + _pluginDataHandler, + _pluginManifestConflictHandler, + _logger, + _templateManagementConfig, + _submodelDescriptorService, + _aasRepositoryService); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasIdAsync_ValidatesOwnershipAndReturnsDescriptor() + { + const string aasId = "aas-1"; + const string submodelId = "submodel-1"; + var expected = new SubmodelDescriptor { Id = submodelId }; + + _submodelDescriptorService.GetSubmodelDescriptorByIdAsync(submodelId, Arg.Any()) + .Returns(expected); + + var result = await _sut.GetSubmodelDescriptorByAasIdAsync(aasId, submodelId, CancellationToken.None); + + Assert.Same(expected, result); + await _aasRepositoryService.Received(1) + .ValidateSubmodelBelongsToAasAsync(aasId, submodelId, Arg.Any()); + await _submodelDescriptorService.Received(1) + .GetSubmodelDescriptorByIdAsync(submodelId, Arg.Any()); + } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasIdAsync_DeduplicatesSubmodelIdsAndAppliesPaging() + { + const string aasId = "aas-1"; + + _aasRepositoryService.GetSubmodelRefByIdAsync(aasId, null, null, Arg.Any()) + .Returns(new SubmodelRef + { + PagingMetaData = new PagingMetaData(), + Result = + [ + new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, "submodel-2")], null), + new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, "submodel-1")], null), + new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, "submodel-2")], null) + ] + }); + + _submodelDescriptorService.GetSubmodelDescriptorByIdAsync("submodel-1", Arg.Any()) + .Returns(new SubmodelDescriptor { Id = "submodel-1" }); + _submodelDescriptorService.GetSubmodelDescriptorByIdAsync("submodel-2", Arg.Any()) + .Returns(new SubmodelDescriptor { Id = "submodel-2" }); + + var result = await _sut.GetAllSubmodelDescriptorsByAasIdAsync(aasId, 1, null, CancellationToken.None); + + Assert.NotNull(result); + Assert.NotNull(result.Result); + Assert.Single(result.Result); + Assert.NotNull(result.PagingMetaData); + Assert.False(string.IsNullOrWhiteSpace(result.PagingMetaData.Cursor)); + await _submodelDescriptorService.Received(1).GetSubmodelDescriptorByIdAsync("submodel-1", Arg.Any()); + await _submodelDescriptorService.Received(1).GetSubmodelDescriptorByIdAsync("submodel-2", Arg.Any()); } [Fact] @@ -363,7 +430,7 @@ public async Task GetAllShellDescriptorsAsync_RespectsMaxConcurrency() var sut = new ShellDescriptorService( _templateProvider, _shellTemplateMappingProvider, _dataHandler, _pluginDataHandler, _pluginManifestConflictHandler, _logger, - Options.Create(config)); + Options.Create(config), _submodelDescriptorService, _aasRepositoryService); var currentConcurrency = 0; var maxObservedConcurrency = 0; diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRepository/AasRepositoryServiceTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRepository/AasRepositoryServiceTests.cs index d7e07d2e..0b6c7c64 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRepository/AasRepositoryServiceTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/AasRepository/AasRepositoryServiceTests.cs @@ -1,13 +1,16 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Plugin; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; using AasCore.Aas3_1; @@ -25,6 +28,7 @@ public class AasRepositoryServiceTests private readonly IAasRepositoryTemplateService _templateService = Substitute.For(); private readonly IPluginDataHandler _pluginDataHandler = Substitute.For(); private readonly IPluginManifestConflictHandler _pluginManifestConflictHandler = Substitute.For(); + private readonly ISubmodelRepositoryService _submodelRepositoryService = Substitute.For(); private readonly ILogger _logger = Substitute.For>(); private readonly IOptions _templateManagementConfig = Substitute.For>(); private readonly IFileContentProvider _fileContentProvider = Substitute.For(); @@ -53,9 +57,75 @@ public AasRepositoryServiceTests() _pluginManifestConflictHandler, _fileContentProvider, _templateManagementConfig, + _submodelRepositoryService, _generalConfig); } + [Fact] + public async Task ValidateSubmodelBelongsToAasAsync_WithMatchingSubmodelId_DoesNotThrow() + { + _templateService.GetSubmodelRefByIdAsync(AasIdentifier, Arg.Any()) + .Returns( + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, "submodel-1")], + null) + ]); + + await _sut.ValidateSubmodelBelongsToAasAsync(AasIdentifier, "submodel-1", CancellationToken.None); + } + + [Fact] + public async Task ValidateSubmodelBelongsToAasAsync_WithoutMatchingSubmodelId_ThrowsSubmodelNotFoundException() + { + _templateService.GetSubmodelRefByIdAsync(AasIdentifier, Arg.Any()) + .Returns( + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, "other-submodel")], + null) + ]); + + await Assert.ThrowsAsync(() => + _sut.ValidateSubmodelBelongsToAasAsync(AasIdentifier, "submodel-1", CancellationToken.None)); + } + + [Fact] + public async Task GetSubmodelByAasIdAsync_WhenSubmodelBelongsToAas_ForwardsLevelAndExtentToSubmodelRepository() + { + const string submodelId = "submodel-1"; + _templateService.GetSubmodelRefByIdAsync(AasIdentifier, Arg.Any()) + .Returns( + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, submodelId)], + null) + ]); + + var expectedSubmodel = new Submodel(submodelId); + _submodelRepositoryService.GetSubmodelAsync( + submodelId, + Arg.Is(o => o.Level == Level.core.ToString() && o.Extent == Extent.withBlobValue.ToString()), + Arg.Any()) + .Returns(expectedSubmodel); + + var result = await _sut.GetSubmodelByAasIdAsync( + AasIdentifier, + submodelId, + new SubmodelQueryOptions(Level.core.ToString(), Extent.withBlobValue.ToString()), + CancellationToken.None); + + Assert.Same(expectedSubmodel, result); + await _submodelRepositoryService.Received(1) + .GetSubmodelAsync( + submodelId, + Arg.Is(o => o.Level == Level.core.ToString() && o.Extent == Extent.withBlobValue.ToString()), + Arg.Any()); + } + [Fact] public async Task GetShellByIdAsync_ShouldReturnShellWithAssetInformation() { diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandlerTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandlerTests.cs index d603e26b..7bc0ba14 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandlerTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandlerTests.cs @@ -55,7 +55,7 @@ public void Extract_ReturnsLeafNode() [Fact] public void FillOut_WithLeafNode_SetsBase64Value() { - var blob = new Blob(contentType: "image/png", idShort: "MyBlob"); + var blob = new Blob(contentType: "image/png", idShort: "MyBlob", value: [9]); var base64 = Convert.ToBase64String(new byte[] { 1, 2, 3 }); var values = new SemanticLeafNode("http://test/blob", base64, DataType.String, Cardinality.One); @@ -65,6 +65,18 @@ public void FillOut_WithLeafNode_SetsBase64Value() Equal([1, 2, 3], blob.Value); } + [Fact] + public void FillOut_WithLeafNodeAndNoTemplateValue_LeavesValueUnset() + { + var blob = new Blob(contentType: "image/png", idShort: "MyBlob"); + var base64 = Convert.ToBase64String(new byte[] { 1, 2, 3 }); + var values = new SemanticLeafNode("http://test/blob", base64, DataType.String, Cardinality.One); + + _sut.FillOut(blob, values, (_, _, _) => { }); + + Null(blob.Value); + } + [Fact] public void FillOut_WithBranchNode_DoesNotModifyValue() { diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryServiceTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryServiceTests.cs index 672b68c6..1653bad4 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryServiceTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryServiceTests.cs @@ -4,8 +4,8 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; -using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Plugin; @@ -130,7 +130,7 @@ public async Task GetSubmodelElementAsync_ReturnsFilledSubmodelElement() var expected = TestData.CreateFilledContactInformation(); _templateService - .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any()) + .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any(), Arg.Any()) .Returns(submodel); var semanticTree = CreateSubmodelTreeNode(""); @@ -142,7 +142,7 @@ public async Task GetSubmodelElementAsync_ReturnsFilledSubmodelElement() .Returns(filledSubmodel); _semanticIdHandler.Extract(filledSubmodel, IdShortPath).Returns(expected); - var result = await _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, CancellationToken.None) as SubmodelElementCollection; + var result = await _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, null, CancellationToken.None) as SubmodelElementCollection; Assert.Equal(expected.SemanticId, result?.SemanticId); Assert.Equal(expected.Value, result?.Value); @@ -157,7 +157,7 @@ public async Task GetSubmodelElementAsync_IdShortWithIndex_ReturnsFilledSubmodel var expected = TestData.CreateFilledContactName(); _templateService - .GetSubmodelTemplateAsync(SubmodelId, IdShortPathWithNestedElement, Arg.Any()) + .GetSubmodelTemplateAsync(SubmodelId, IdShortPathWithNestedElement, Arg.Any(), Arg.Any()) .Returns(submodel); var semanticTree = CreateSubmodelTreeNode(""); @@ -169,7 +169,7 @@ public async Task GetSubmodelElementAsync_IdShortWithIndex_ReturnsFilledSubmodel .Returns(filledSubmodel); _semanticIdHandler.Extract(filledSubmodel, IdShortPathWithNestedElement).Returns(expected); - var result = await _sut.GetSubmodelElementAsync(SubmodelId, IdShortPathWithNestedElement, CancellationToken.None) as Property; + var result = await _sut.GetSubmodelElementAsync(SubmodelId, IdShortPathWithNestedElement, null, CancellationToken.None) as Property; Assert.Equal(expected.SemanticId, result?.SemanticId); Assert.Equal(expected.Value, result?.Value); @@ -212,11 +212,11 @@ await Assert.ThrowsAsync(() => public async Task GetSubmodelElementAsync_WhenResourceNotFound_ThrowsSubmodelElementNotFoundException() { _templateService - .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any()) + .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any(), Arg.Any()) .ThrowsAsync(new ResourceNotFoundException()); await Assert.ThrowsAsync(() => - _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, CancellationToken.None)); + _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, null, CancellationToken.None)); } [Fact] @@ -227,7 +227,7 @@ public async Task GetSubmodelElementAsync_WhenResponseParsingFails_ThrowsInterna .ThrowsAsync(new ResponseParsingException()); await Assert.ThrowsAsync(() => - _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, CancellationToken.None)); + _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, null, CancellationToken.None)); } [Fact] @@ -238,7 +238,7 @@ public async Task GetSubmodelElementAsync_WhenRequestTimesOut_ThrowsPluginNotAva .ThrowsAsync(new RequestTimeoutException()); await Assert.ThrowsAsync(() => - _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, CancellationToken.None)); + _sut.GetSubmodelElementAsync(SubmodelId, IdShortPath, null, CancellationToken.None)); } public static SemanticBranchNode CreateSubmodelTreeNode(string value) @@ -594,7 +594,7 @@ await Assert.ThrowsAsync(() => private void ArrangeAttachmentElement(string idShortPath, ISubmodelElement element) { var template = TestData.CreateSubmodelWithElement(element, idShortPath); - _templateService.GetSubmodelTemplateAsync(SubmodelId, idShortPath, Arg.Any()).Returns(template); + _templateService.GetSubmodelTemplateAsync(SubmodelId, idShortPath, Arg.Any(), Arg.Any()).Returns(template); _semanticIdHandler.Extract(Arg.Any()).Returns(CreateSubmodelTreeNode("")); _pluginDataHandler.TryGetValuesAsync(Arg.Any>(), Arg.Any(), SubmodelId, Arg.Any()).Returns(CreateSubmodelTreeNode("")); _semanticIdHandler.FillOutTemplate(Arg.Any(), Arg.Any()).Returns(template); @@ -644,7 +644,7 @@ public async Task GetFileAttachmentAsync_WhenSubmodelNotFound_ThrowsSubmodelElem const string IdShortPath = "Documents.ProductImage"; _templateService - .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any()) + .GetSubmodelTemplateAsync(SubmodelId, IdShortPath, Arg.Any(), Arg.Any()) .ThrowsAsync(new ResourceNotFoundException()); await Assert.ThrowsAsync(() => diff --git a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateServiceTests.cs b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateServiceTests.cs index f022df4f..11d29ca9 100644 --- a/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateServiceTests.cs +++ b/source/AAS.TwinEngine.DataEngine.UnitTests/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateServiceTests.cs @@ -74,7 +74,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsNull_WhenElementNotFound() _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, CancellationToken.None)); + var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, null, CancellationToken.None)); Assert.Equal("Submodel Element not found.", exception.Message); } @@ -83,7 +83,7 @@ public async Task GetSubmodelTemplateAsync_ThrowsException_WhenSubmodelIdIsInval { string? submodelId = null; - var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(submodelId!, "idShort", CancellationToken.None)); + var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(submodelId!, "idShort", null, CancellationToken.None)); Assert.Equal("Internal Server Error.", exception.Message); } @@ -97,7 +97,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsElement_WhenSingleProperty() _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, null, CancellationToken.None); Assert.Equal(GetSemanticId(expectedElement), GetSemanticId(result)); Assert.Equal(expectedElement.SubmodelElements!.Count, result.SubmodelElements!.Count); Assert.Single(expectedElement.SubmodelElements); @@ -113,7 +113,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsCustomSubmodel_WhenNestedPrope _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, null, CancellationToken.None); Assert.Equal(GetSemanticId(expectedElement), GetSemanticId(result)); Assert.Equal(expectedElement.SubmodelElements!.Count, result.SubmodelElements!.Count); @@ -131,7 +131,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsSubmodelElementNotFoundExcepti _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, CancellationToken.None)); + var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, IdShortPath, null, CancellationToken.None)); } [Fact] @@ -139,7 +139,7 @@ public async Task GetSubmodelElementTemplateAsync_ThrowsBadRequestException_When { const string IdShortPath = "ContactInformation0"; - var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync("", IdShortPath, CancellationToken.None)); + var exception = await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync("", IdShortPath, null, CancellationToken.None)); Assert.IsType(exception); } @@ -152,7 +152,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsSubmodel_WhenPathContainsListI _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, path, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, path, null, CancellationToken.None); Assert.Equal(GetSemanticId(expectedSubmodel), GetSemanticId(result)); @@ -176,7 +176,7 @@ public async Task GetSubmodelTemplateAsync_WithListIndexPath_ReturnsSubmodelWith _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(expectedSubmodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None); Assert.Equal(GetSemanticId(expectedSubmodel), GetSemanticId(result)); @@ -200,7 +200,7 @@ public async Task GetSubmodelTemplateAsync_Supports_UrlEncoded_ListIndex() _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(submodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None); Assert.NotNull(result); } @@ -216,7 +216,7 @@ public async Task GetSubmodelTemplateAsync_Throws_When_ListIndex_IsNegative() .Returns(submodel); await Assert.ThrowsAsync( - () => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None)); + () => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None)); } [Fact] @@ -229,7 +229,7 @@ public async Task GetSubmodelTemplateAsync_ReturnsSubmodel_WhenTypeValueListElem _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(submodel); - var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None); + var result = await _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None); Assert.Equal(GetSemanticId(expectedSubmodel), GetSemanticId(result)); @@ -253,7 +253,7 @@ public async Task GetSubmodelTemplateAsync_ThrowsInternalDataProcessingException _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(submodel); - await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None)); + await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None)); } [Fact] @@ -265,7 +265,7 @@ public async Task GetSubmodelTemplateAsync_ThrowsNotFoundException_WhenPathSegme _templateProvider.GetFilteredSubmodelTemplateAsync(TemplateId, null, Arg.Any()) .Returns(submodel); - await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, CancellationToken.None)); + await Assert.ThrowsAsync(() => _sut.GetSubmodelTemplateAsync(SubmodelId, Path, null, CancellationToken.None)); } [Fact] @@ -322,7 +322,7 @@ public async Task GetSubmodelTemplateAsync_WithIdShortPath_ThrowsSubmodelElement .ThrowsAsync(new ResourceNotFoundException()); await Assert.ThrowsAsync( - () => _sut.GetSubmodelTemplateAsync(SubmodelId, "SomePath", CancellationToken.None)); + () => _sut.GetSubmodelTemplateAsync(SubmodelId, "SomePath", null, CancellationToken.None)); } #region GetFilteredSubmodelTemplateAsync diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/IShellDescriptorHandler.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/IShellDescriptorHandler.cs index 30a09d53..3f4ebcb5 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/IShellDescriptorHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/IShellDescriptorHandler.cs @@ -1,5 +1,6 @@ using AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Responses; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.Responses; namespace AAS.TwinEngine.DataEngine.Api.AasRegistry.Handler; @@ -8,4 +9,8 @@ public interface IShellDescriptorHandler Task GetAllShellDescriptors(GetShellDescriptorsRequest request, CancellationToken cancellationToken); Task GetShellDescriptorById(GetShellDescriptorRequest request, CancellationToken cancellationToken); + + Task GetAllSubmodelDescriptorsByAasId(GetSubmodelDescriptorsByAasRequest request, CancellationToken cancellationToken); + + Task GetSubmodelDescriptorByAasId(GetSubmodelDescriptorByAasRequest request, CancellationToken cancellationToken); } diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/ShellDescriptorHandler.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/ShellDescriptorHandler.cs index b3eb23fc..ff4397b6 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/ShellDescriptorHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Handler/ShellDescriptorHandler.cs @@ -1,6 +1,8 @@ using AAS.TwinEngine.DataEngine.Api.AasRegistry.MappingProfiles; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Responses; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.MappingProfiles; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRegistry; @@ -16,7 +18,7 @@ public Task GetAllShellDescriptors(GetShellDescriptorsReque request?.Limit.ValidateLimit(logger); request?.Cursor?.ValidateCursor(logger); - return GetShellDescriptorResourceAsync( + return GetResourceAsync( null, "shell descriptors", _ => shellDescriptorService.GetAllShellDescriptorsAsync(request.Limit, request.Cursor, cancellationToken), @@ -25,14 +27,57 @@ public Task GetAllShellDescriptors(GetShellDescriptorsReque } public Task GetShellDescriptorById(GetShellDescriptorRequest request, CancellationToken cancellationToken) - => GetShellDescriptorResourceAsync( + => GetResourceAsync( request?.AasIdentifier, "shell descriptor", id => shellDescriptorService.GetShellDescriptorByIdAsync(id!, cancellationToken), descriptor => descriptor.ToDto() ); - private async Task GetShellDescriptorResourceAsync( + public Task GetAllSubmodelDescriptorsByAasId(GetSubmodelDescriptorsByAasRequest request, CancellationToken cancellationToken) + { + request?.Limit.ValidateLimit(logger); + request?.Cursor?.ValidateCursor(logger); + + return GetResourceAsync( + request?.AasIdentifier, + "submodel descriptors by AasId", + aasId => shellDescriptorService.GetAllSubmodelDescriptorsByAasIdAsync( + aasId, + request.Limit, + request.Cursor, + cancellationToken), + descriptors => descriptors.ToDto()); + } + + public Task GetSubmodelDescriptorByAasId(GetSubmodelDescriptorByAasRequest request, CancellationToken cancellationToken) + { + return GetResourceAsync( + request?.AasIdentifier, + request?.SubmodelIdentifier, + "submodel descriptor", + (aasId, submodelId) => shellDescriptorService.GetSubmodelDescriptorByAasIdAsync(aasId, submodelId, cancellationToken), + descriptor => descriptor.ToDto()); + } + + private async Task GetResourceAsync( + string? encodedAasId, + string? encodedSubmodelId, + string resourceName, + Func> fetchFunc, + Func mapFunc) + { + var decodedAasId = encodedAasId?.DecodeBase64Url(logger); + var decodedSubmodelId = encodedSubmodelId?.DecodeBase64Url(logger); + + logger.LogInformation("Get {ResourceName} for AAS: {AasId}, Submodel: {SubmodelId}", resourceName, decodedAasId, decodedSubmodelId); + + var result = await fetchFunc(decodedAasId, decodedSubmodelId).ConfigureAwait(false); + ValidateResourceExists(result, resourceName); + return mapFunc(result); + } + + private async Task GetResourceAsync( string? encodedId, string resourceName, Func> serviceFetchFunc, @@ -49,7 +94,7 @@ private async Task GetShellDescriptorResourceAsync( private void LogRequestStart(string resourceName, string? decodedId) { - if (resourceName is "shell descriptor") + if (resourceName is "shell descriptors") { logger.LogInformation("Start executing get request for {ResourceName}", resourceName); } diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorByAasRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorByAasRequest.cs new file mode 100644 index 00000000..34178ee3 --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorByAasRequest.cs @@ -0,0 +1,3 @@ +namespace AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; + +public record GetSubmodelDescriptorByAasRequest(string AasIdentifier, string SubmodelIdentifier); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorsByAasRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorsByAasRequest.cs new file mode 100644 index 00000000..06c1a7cf --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/Requests/GetSubmodelDescriptorsByAasRequest.cs @@ -0,0 +1,3 @@ +namespace AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; + +public record GetSubmodelDescriptorsByAasRequest(string AasIdentifier, int? Limit, string? Cursor); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/ShellDescriptorController.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/ShellDescriptorController.cs index 07b34467..f49557e8 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/ShellDescriptorController.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRegistry/ShellDescriptorController.cs @@ -4,6 +4,7 @@ using AAS.TwinEngine.DataEngine.Api.AasRegistry.Handler; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Requests; using AAS.TwinEngine.DataEngine.Api.AasRegistry.Responses; +using AAS.TwinEngine.DataEngine.Api.SubmodelRegistry.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Responses; using Asp.Versioning; @@ -67,4 +68,58 @@ public async Task> GetShellDescriptorByIdAsync( var response = await shellDescriptorHandler.GetShellDescriptorById(request, cancellationToken).ConfigureAwait(false); return Ok(response); } + + /// + /// Returns all Submodel Descriptors + /// + /// The Asset Administration Shell's unique id (UTF8-BASE64-URL-encoded) + /// The maximum number of elements in the response array + /// A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue + /// Cancellation token + /// Requested Submodel Descriptors + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodel-descriptors")] + [ProducesResponseType(typeof(SubmodelDescriptorsDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> GetAllSubmodelDescriptorsByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromQuery] int? limit, + [FromQuery] string? cursor, + CancellationToken cancellationToken) + { + logger.LogInformation("Get All Submodel Descriptors through superpath for AAS"); + var request = new GetSubmodelDescriptorsByAasRequest(aasIdentifier, limit, cursor); + var response = await shellDescriptorHandler.GetAllSubmodelDescriptorsByAasId(request, cancellationToken).ConfigureAwait(false); + return Ok(response); + } + + /// + /// Returns a specific Submodel Descriptor + /// + /// The Asset Administration Shell's unique id (UTF8-BASE64-URL-encoded) + /// The Submodel's unique id (UTF8-BASE64-URL-encoded) + /// Cancellation token + /// Requested Submodel Descriptor + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodel-descriptors/{submodelIdentifier}")] + [ProducesResponseType(typeof(SubmodelDescriptorDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> GetSubmodelDescriptorByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromRoute] string submodelIdentifier, + CancellationToken cancellationToken) + { + logger.LogInformation("Get Submodel Descriptor through superpath for AAS"); + var request = new GetSubmodelDescriptorByAasRequest(aasIdentifier, submodelIdentifier); + var response = await shellDescriptorHandler.GetSubmodelDescriptorByAasId(request, cancellationToken).ConfigureAwait(false); + return Ok(response); + } } diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/AasRepositoryController.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/AasRepositoryController.cs index a64e009b..e2697f8f 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/AasRepositoryController.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/AasRepositoryController.cs @@ -6,6 +6,8 @@ using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses; using AAS.TwinEngine.DataEngine.Api.Shared; using AAS.TwinEngine.DataEngine.Api.Shared.Results; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Responses; using AasCore.Aas3_1; @@ -49,7 +51,8 @@ public async Task> GetShellsByAssetIdAsync( CancellationToken cancellationToken) { logger.LogInformation("Start request to get shells (assetIds/idShort filters)"); - var response = await aasRepositoryHandler.GetShellsByAssetIdsAsync(assetIds, idShort, limit, cursor, cancellationToken).ConfigureAwait(false); + var request = new GetShellsByAssetIdsRequest(assetIds, idShort, limit, cursor); + var response = await aasRepositoryHandler.GetShellsByAssetIdsAsync(request, cancellationToken).ConfigureAwait(false); return Ok(response); } @@ -143,4 +146,128 @@ public async Task> GetSubmodelRefByIdAsync([FromRoute] var response = await aasRepositoryHandler.GetSubmodelRefByIdAsync(request, cancellationToken).ConfigureAwait(false); return Ok(response); } + + /// + /// Returns the Submodel + /// + /// The Asset Administration Shell’s unique id (UTF8-BASE64-URL-encoded) + /// The Submodel’s unique id (UTF8-BASE64-URL-encoded) + /// Determines the structural depth of the respective resource content. + /// Determines to which extent the resource is being serialized. + /// Cancellation token. + /// Requested Submodel + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodels/{submodelIdentifier}")] + [ProducesResponseType(typeof(Submodel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.InternalServerError)] + public async Task> GetSubmodelByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromRoute] string submodelIdentifier, + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) + { + logger.LogInformation("Get Submodel through superpath for AAS"); + var request = new GetSubmodelByAasRequest(aasIdentifier, submodelIdentifier, level, extent); + var response = await aasRepositoryHandler.GetSubmodelByAasIdAsync(request, cancellationToken).ConfigureAwait(false); + return Ok(Jsonization.Serialize.ToJsonObject(response)); + } + + /// + /// Returns all submodel elements including their hierarchy + /// + /// The Asset Administration Shell's unique id (UTF8-BASE64-URL-encoded) + /// The Submodel's unique id (UTF8-BASE64-URL-encoded) + /// The maximum number of elements in the response array + /// A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue + /// Determines the structural depth of the respective resource content. + /// Determines to which extent the resource is being serialized. + /// Cancellation token. + /// List of found submodel elements + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodels/{submodelIdentifier}/submodel-elements")] + [ProducesResponseType(typeof(SubmodelElementsDto), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status500InternalServerError)] + public async Task> GetAllSubmodelElementsByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromRoute] string submodelIdentifier, + [FromQuery] int? limit, + [FromQuery] string? cursor, + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) + { + logger.LogInformation("Get All Submodel Elements through superpath for AAS"); + var request = new GetAllSubmodelElementsByAasRequest(aasIdentifier, submodelIdentifier, limit, cursor, level, extent); + var response = await aasRepositoryHandler.GetAllSubmodelElementsByAasIdAsync(request, cancellationToken).ConfigureAwait(false); + return Ok(response); + } + + /// + /// Returns a specific submodel element from the Submodel at a specified path + /// + /// The Asset Administration Shell's unique id (UTF8-BASE64-URL-encoded) + /// The Submodel's unique id (UTF8-BASE64-URL-encoded) + /// IdShort path to the submodel element (dot-notation) + /// Determines the structural depth of the respective resource content. + /// Determines to which extent the resource is being serialized. + /// Cancellation token. + /// Requested submodel element + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}")] + [ProducesResponseType(typeof(ISubmodelElement), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status500InternalServerError)] + public async Task> GetSubmodelElementByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromRoute] string submodelIdentifier, + [FromRoute] string idShortPath, + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) + { + logger.LogInformation("Get Submodel Element through superpath for AAS"); + var request = new GetSubmodelElementByAasRequest(aasIdentifier, submodelIdentifier, idShortPath, level, extent); + var response = await aasRepositoryHandler.GetSubmodelElementByAasIdAsync(request, cancellationToken).ConfigureAwait(false); + return Ok(Jsonization.Serialize.ToJsonObject(response)); + } + + /// + /// Downloads file content from a specific submodel element from the Submodel at a specified path + /// + /// The Asset Administration Shell's unique id (UTF8-BASE64-URL-encoded) + /// The Submodel's unique id (UTF8-BASE64-URL-encoded) + /// IdShort path to the submodel element (dot-notation) + /// Cancellation token. + /// Requested file + /// Bad Request, e.g. the request parameters of the format of the request body is wrong. + /// Not Found + /// Internal Server Error + [HttpGet("{aasIdentifier}/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/attachment")] + [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ServiceErrorResponse), StatusCodes.Status500InternalServerError)] + public async Task GetFileByPathByAasIdAsync( + [FromRoute] string aasIdentifier, + [FromRoute] string submodelIdentifier, + [FromRoute] string idShortPath, + CancellationToken cancellationToken) + { + logger.LogInformation("Get File through superpath for AAS"); + var request = new GetFileByPathByAasIdRequest(aasIdentifier, submodelIdentifier, idShortPath); + var attachment = await aasRepositoryHandler.GetFileByPathByAasIdAsync(request, cancellationToken).ConfigureAwait(false); + return new FileContentStreamResult(attachment, ContentDispositionType.attachment); + } } diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/AasRepositoryHandler.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/AasRepositoryHandler.cs index 2217bdc3..276fcddd 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/AasRepositoryHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/AasRepositoryHandler.cs @@ -1,14 +1,17 @@ -using System.Text.Json; +using System.Text.Json; using AAS.TwinEngine.DataEngine.Api.AasRepository.MappingProfiles; using AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses; using AAS.TwinEngine.DataEngine.Api.Shared; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.MappingProfiles; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Responses; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AasCore.Aas3_1; @@ -18,24 +21,23 @@ public class AasRepositoryHandler( ILogger logger, IAasRepositoryService aasRepositoryService) : IAasRepositoryHandler { - public async Task GetShellsByAssetIdsAsync( - string[]? assetIds, string? idShort, int? limit, string? cursor, CancellationToken cancellationToken) + public async Task GetShellsByAssetIdsAsync(GetShellsByAssetIdsRequest request, CancellationToken cancellationToken) { - limit.ValidateLimit(logger); - cursor?.ValidateCursor(logger); + request?.Limit.ValidateLimit(logger); + request?.Cursor?.ValidateCursor(logger); - var assetIdsFilters = assetIds is not null && assetIds.Length > 0 - ? AssetIdHelper.DecodeAssetIds(assetIds, logger) + var assetIdsFilters = request?.AssetIds is not null && request.AssetIds.Length > 0 + ? AssetIdHelper.DecodeAssetIds(request.AssetIds, logger) : null; var filter = new ShellSearchFilter { SpecificAssetIds = assetIdsFilters, - IdShort = idShort + IdShort = request?.IdShort }; var shells = await aasRepositoryService - .GetShellsByFiltersAsync(filter, limit, cursor, cancellationToken) + .GetShellsByFiltersAsync(filter, request?.Limit, request?.Cursor, cancellationToken) .ConfigureAwait(false); return shells.ToDto(); @@ -74,6 +76,58 @@ public Task GetThumbnailAsync(GetThumbnailRequest request, "thumbnail", id => aasRepositoryService.GetThumbnailAsync(id, cancellationToken)); + public Task GetSubmodelByAasIdAsync(GetSubmodelByAasRequest request, CancellationToken cancellationToken) + { + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); + + return GetResourceByIdAsync( + request?.AasIdentifier, + request?.SubmodelId, + "submodel By AasID", + async (aasId, submodelId) => await aasRepositoryService.GetSubmodelByAasIdAsync(aasId, submodelId, queryOptions, cancellationToken).ConfigureAwait(false)); + } + + public Task GetAllSubmodelElementsByAasIdAsync(GetAllSubmodelElementsByAasRequest request, CancellationToken cancellationToken) + { + request?.Limit.ValidateLimit(logger); + request?.Cursor?.ValidateCursor(logger); + + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); + + return GetResourceByIdAsync( + request?.AasIdentifier, + request?.SubmodelId, + "submodel elements By AasID", + async (aasId, submodelId) => (await aasRepositoryService + .GetAllSubmodelElementsByAasIdAsync(aasId, submodelId, queryOptions, request?.Limit, request?.Cursor, cancellationToken) + .ConfigureAwait(false)).ToDto()); + } + + public Task GetSubmodelElementByAasIdAsync(GetSubmodelElementByAasRequest request, CancellationToken cancellationToken) + { + var decodedIdShortPath = Uri.UnescapeDataString(request?.IdShortPath ?? string.Empty); + decodedIdShortPath.ValidateIdShortPath(nameof(request.IdShortPath), logger); + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); + + return GetResourceByIdAsync( + request?.AasIdentifier, + request?.SubmodelId, + "submodel element By AasID", + async (aasId, submodelId) => await aasRepositoryService.GetSubmodelElementByAasIdAsync(aasId, submodelId, decodedIdShortPath, queryOptions, cancellationToken).ConfigureAwait(false)); + } + + public Task GetFileByPathByAasIdAsync(GetFileByPathByAasIdRequest request, CancellationToken cancellationToken) + { + var decodedIdShortPath = Uri.UnescapeDataString(request?.IdShortPath ?? string.Empty); + decodedIdShortPath.ValidateIdShortPath(nameof(request.IdShortPath), logger); + + return GetResourceByIdAsync( + request?.AasIdentifier, + request?.SubmodelIdentifier, + "file By AasID", + async (aasId, submodelId) => await aasRepositoryService.GetFileAttachmentByAasIdAsync(aasId, submodelId, decodedIdShortPath, cancellationToken).ConfigureAwait(false)); + } + private Task GetResourceByIdAsync( string? encodedId, string resourceName, @@ -95,6 +149,25 @@ private async Task GetResourceByIdAsync( return mapFunc(result!); } + private async Task GetResourceByIdAsync( + string? encodedAasId, + string? encodedSubmodelId, + string resourceName, + Func> fetchFunc) + { + var decodedAasId = encodedAasId?.DecodeBase64Url(logger); + var decodedSubmodelId = encodedSubmodelId?.DecodeBase64Url(logger); + + logger.LogInformation("Start executing get request for {ResourceName}. AAS: {AasId}, Submodel: {SubmodelId}", resourceName, decodedAasId, decodedSubmodelId); + + var result = await fetchFunc(decodedAasId, decodedSubmodelId) + .ConfigureAwait(false); + + ValidateResourceExists(result, resourceName, decodedSubmodelId); + + return result; + } + private void ValidateResourceExists(T? result, string resourceName, string decodedId) { if (result is null) diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/IAasRepositoryHandler.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/IAasRepositoryHandler.cs index 00131a39..ec065f81 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/IAasRepositoryHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Handler/IAasRepositoryHandler.cs @@ -1,7 +1,8 @@ -using System.Text.Json; +using System.Text.Json; using AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses; +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Responses; using AAS.TwinEngine.DataEngine.DomainModel.Shared; using AasCore.Aas3_1; @@ -10,7 +11,7 @@ namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Handler; public interface IAasRepositoryHandler { - Task GetShellsByAssetIdsAsync(string[]? assetIds, string? idShort, int? limit, string? cursor, CancellationToken cancellationToken); + Task GetShellsByAssetIdsAsync(GetShellsByAssetIdsRequest request, CancellationToken cancellationToken); Task GetShellByIdAsync(GetShellRequest request, CancellationToken cancellationToken); @@ -18,5 +19,13 @@ public interface IAasRepositoryHandler Task GetSubmodelRefByIdAsync(GetSubmodelRefRequest request, CancellationToken cancellationToken); + Task GetSubmodelByAasIdAsync(GetSubmodelByAasRequest request, CancellationToken cancellationToken); + + Task GetAllSubmodelElementsByAasIdAsync(GetAllSubmodelElementsByAasRequest request, CancellationToken cancellationToken); + + Task GetSubmodelElementByAasIdAsync(GetSubmodelElementByAasRequest request, CancellationToken cancellationToken); + + Task GetFileByPathByAasIdAsync(GetFileByPathByAasIdRequest request, CancellationToken cancellationToken); + Task GetThumbnailAsync(GetThumbnailRequest request, CancellationToken cancellationToken); } diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetAllSubmodelElementsByAasRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetAllSubmodelElementsByAasRequest.cs new file mode 100644 index 00000000..26726822 --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetAllSubmodelElementsByAasRequest.cs @@ -0,0 +1,11 @@ +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; + +namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; + +public record GetAllSubmodelElementsByAasRequest( + string AasIdentifier, + string SubmodelId, + int? Limit, + string? Cursor, + Level Level, + Extent Extent); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetFileByPathByAasIdRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetFileByPathByAasIdRequest.cs new file mode 100644 index 00000000..6c7489d6 --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetFileByPathByAasIdRequest.cs @@ -0,0 +1,3 @@ +namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; + +public record GetFileByPathByAasIdRequest(string AasIdentifier, string SubmodelIdentifier, string IdShortPath); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetShellsByAssetIdsRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetShellsByAssetIdsRequest.cs new file mode 100644 index 00000000..c2d2c4d1 --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetShellsByAssetIdsRequest.cs @@ -0,0 +1,3 @@ +namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; + +public record GetShellsByAssetIdsRequest(string[]? AssetIds, string? IdShort, int? Limit, string? Cursor); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelByAasRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelByAasRequest.cs new file mode 100644 index 00000000..9d3f284b --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelByAasRequest.cs @@ -0,0 +1,5 @@ +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; + +namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; + +public record GetSubmodelByAasRequest(string AasIdentifier, string SubmodelId, Level Level, Extent Extent); diff --git a/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelElementByAasRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelElementByAasRequest.cs new file mode 100644 index 00000000..16c5fd5f --- /dev/null +++ b/source/AAS.TwinEngine.DataEngine/Api/AasRepository/Requests/GetSubmodelElementByAasRequest.cs @@ -0,0 +1,5 @@ +using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; + +namespace AAS.TwinEngine.DataEngine.Api.AasRepository.Requests; + +public record GetSubmodelElementByAasRequest(string AasIdentifier, string SubmodelId, string IdShortPath, Level Level, Extent Extent); diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Handler/SubmodelRepositoryHandler.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Handler/SubmodelRepositoryHandler.cs index 46547301..e3fb4f9a 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Handler/SubmodelRepositoryHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Handler/SubmodelRepositoryHandler.cs @@ -17,7 +17,7 @@ public class SubmodelRepositoryHandler( { public Task GetSubmodel(GetSubmodelRequest request, CancellationToken cancellationToken) { - var queryOptions = request?.Level is not null || request?.Extent is not null ? new SubmodelQueryOptions(request.Level?.ToString(), request.Extent?.ToString()) : null; + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); return GetResourceByIdAsync( request?.SubmodelId, @@ -30,10 +30,12 @@ public Task GetSubmodelElement(GetSubmodelElementRequest reque var decodedIdShortPath = Uri.UnescapeDataString(request?.IdShortPath ?? string.Empty); decodedIdShortPath.ValidateIdShortPath(nameof(request.IdShortPath), logger); + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); + return GetResourceByIdAsync( request?.SubmodelId, "submodel element", - id => submodelRepositoryService.GetSubmodelElementAsync(id, decodedIdShortPath, cancellationToken)!); + id => submodelRepositoryService.GetSubmodelElementAsync(id, decodedIdShortPath, queryOptions, cancellationToken)); } public async Task GetAllSubmodels(GetAllSubmodelsRequest request, CancellationToken cancellationToken) @@ -47,7 +49,7 @@ public async Task GetAllSubmodels(GetAllSubmodelsRequest request, IdShort = request?.IdShort }; - var queryOptions = request?.Level is not null || request?.Extent is not null ? new SubmodelQueryOptions(request.Level?.ToString(), request.Extent?.ToString()) : null; + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); var result = await submodelRepositoryService.GetAllSubmodelsAsync(filter, queryOptions, request?.Limit, request?.Cursor, cancellationToken).ConfigureAwait(false); @@ -59,9 +61,7 @@ public async Task GetAllSubmodelElements(GetAllSubmodelElem request?.Limit.ValidateLimit(logger); request?.Cursor?.ValidateCursor(logger); - var queryOptions = request?.Level is not null || request?.Extent is not null - ? new SubmodelQueryOptions(request.Level?.ToString(), request.Extent?.ToString()) - : null; + var queryOptions = new SubmodelQueryOptions(request?.Level.ToString(), request?.Extent.ToString()); var result = await GetResourceByIdAsync( request?.SubmodelId, diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelElementsRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelElementsRequest.cs index c2d8397d..6a490961 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelElementsRequest.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelElementsRequest.cs @@ -1,23 +1,3 @@ namespace AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; -public record GetAllSubmodelElementsRequest -{ - public string? SubmodelId { get; init; } - - public int? Limit { get; init; } - - public string? Cursor { get; init; } - - public Level? Level { get; init; } - - public Extent? Extent { get; init; } - - public GetAllSubmodelElementsRequest(string? submodelId, int? limit, string? cursor, Level? level = null, Extent? extent = null) - { - SubmodelId = submodelId; - Limit = limit; - Cursor = cursor; - Level = level; - Extent = extent; - } -} +public record GetAllSubmodelElementsRequest(string? SubmodelId, int? Limit, string? Cursor, Level Level, Extent Extent); diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelsRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelsRequest.cs index 1aae2ddb..50f04779 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelsRequest.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetAllSubmodelsRequest.cs @@ -25,4 +25,4 @@ public enum Extent { withBlobValue, withoutBlobValue -} \ No newline at end of file +} diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelElementRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelElementRequest.cs index 582f56e2..194806ce 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelElementRequest.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelElementRequest.cs @@ -1,3 +1,3 @@ namespace AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; -public record GetSubmodelElementRequest(string SubmodelId, string IdShortPath); +public record GetSubmodelElementRequest(string SubmodelId, string IdShortPath, Level? Level = Level.deep, Extent? Extent = Extent.withoutBlobValue); \ No newline at end of file diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelRequest.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelRequest.cs index 35f00e87..eca1d23f 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelRequest.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/Requests/GetSubmodelRequest.cs @@ -1,10 +1,3 @@ namespace AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; -public record GetSubmodelRequest(string submodelId) -{ - public string SubmodelId { get; } = submodelId; - - public Level? Level { get; set; } - - public Extent? Extent { get; set; } -} +public record GetSubmodelRequest(string SubmodelId, Level? Level = Level.deep, Extent? Extent = Extent.withoutBlobValue); diff --git a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/SubmodelRepositoryController.cs b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/SubmodelRepositoryController.cs index 941b9a80..e4490393 100644 --- a/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/SubmodelRepositoryController.cs +++ b/source/AAS.TwinEngine.DataEngine/Api/SubmodelRepository/SubmodelRepositoryController.cs @@ -1,11 +1,11 @@ -using System.ComponentModel; -using System.Net; +using System.Net; using System.Text.Json.Nodes; +using AAS.TwinEngine.DataEngine.Api.Shared; +using AAS.TwinEngine.DataEngine.Api.Shared.Results; using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Handler; using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests; using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Responses; -using AAS.TwinEngine.DataEngine.Api.Shared.Results; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Responses; using AasCore.Aas3_1; @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Mvc; using NSwag.Annotations; -using AAS.TwinEngine.DataEngine.Api.Shared; namespace AAS.TwinEngine.DataEngine.Api.SubmodelRepository; @@ -35,8 +34,8 @@ public class SubmodelRepositoryController( /// The Asset Administration Shell's IdShort. /// The maximum number of elements in the response array. /// A server-generated identifier retrieved from pagingMetadata. - /// Determines the structural depth of the returned content. Accepted values: deep, core. - /// Determines the serialization of the returned content. Accepted values: withBlobValue, withoutBlobValue. + /// Determines the structural depth of the returned content. + /// Determines the serialization of the returned content. /// Returns the requested Submodels. /// Bad Request, e.g. the request parameters or request format are invalid. /// Not Found. @@ -51,21 +50,13 @@ public async Task> GetAllSubmodelsAsync( [FromQuery] string? idShort, [FromQuery] int? limit, [FromQuery] string? cursor, - [FromQuery] Level? level, - [FromQuery] Extent? extent, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) { logger.LogInformation("Get All Submodels"); - var request = new GetAllSubmodelsRequest - { - SemanticId = semanticId, - IdShort = idShort, - Limit = limit, - Cursor = cursor, - Level = level, - Extent = extent - }; + var request = new GetAllSubmodelsRequest{SemanticId = semanticId, IdShort = idShort, Limit = limit, Cursor = cursor, Level = level, Extent = extent}; var response = await submodelRepositoryHandler .GetAllSubmodels(request, cancellationToken) @@ -78,8 +69,8 @@ public async Task> GetAllSubmodelsAsync( /// Returns a specific Submodel. /// /// The Submodel's unique id (UTF8-BASE64-URL-encoded) - /// Determines the structural depth of the returned content. Accepted values: deep, core. - /// Controls how blob values are serialized. Accepted values: withBlobValue, withoutBlobValue. + /// Determines the structural depth of the returned content. + /// Controls how blob values are serialized. /// Requested Submodel /// Bad Request, e.g.the request parameters of the format of the request body is wrong. /// Not Found @@ -91,12 +82,12 @@ public async Task> GetAllSubmodelsAsync( [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.InternalServerError)] public async Task> GetSubmodelAsync( [FromRoute] string submodelIdentifier, - [FromQuery] Level? level, - [FromQuery] Extent? extent, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) { logger.LogInformation("Get Submodel"); - var request = new GetSubmodelRequest(submodelIdentifier) { Level = level, Extent = extent }; + var request = new GetSubmodelRequest(submodelIdentifier, level, extent); var response = await submodelRepositoryHandler.GetSubmodel(request, cancellationToken).ConfigureAwait(false); return Ok(Jsonization.Serialize.ToJsonObject(response)); } @@ -107,8 +98,8 @@ public async Task> GetSubmodelAsync( /// The Submodel's unique id (UTF8-BASE64-URL-encoded) /// The maximum number of elements in the response array. /// A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue. - /// Determines the structural depth of the returned content. Accepted values: deep, core. - /// Determines the serialization of the returned content. Accepted values: withBlobValue, withoutBlobValue. + /// Determines the structural depth of the returned content. + /// Determines the serialization of the returned content. /// List of found submodel elements /// Bad Request, e.g. the request parameters or request format are invalid. /// Not Found @@ -122,9 +113,9 @@ public async Task> GetAllSubmodelElementsAsync [FromRoute] string submodelIdentifier, [FromQuery] int? limit, [FromQuery] string? cursor, - [FromQuery] Level? level, - [FromQuery] Extent? extent, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) { logger.LogInformation("Get All Submodel Elements"); var request = new GetAllSubmodelElementsRequest(submodelIdentifier, limit, cursor, level, extent); @@ -137,6 +128,8 @@ public async Task> GetAllSubmodelElementsAsync /// /// The Submodel's unique id (UTF8-BASE64-URL-encoded) /// The IdShort path to the submodel element (dot-separated) + /// Determines the structural depth of the returned content. + /// Controls how blob values are serialized. /// Requested submodel element /// Bad Request, e.g.the request parameters of the format of the request body is wrong. /// Not Found @@ -146,10 +139,15 @@ public async Task> GetAllSubmodelElementsAsync [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.NotFound)] [ProducesResponseType(typeof(ServiceErrorResponse), (int)HttpStatusCode.InternalServerError)] - public async Task> GetSubmodelElementAsync([FromRoute] string submodelIdentifier, [FromRoute] string idShortPath, CancellationToken cancellationToken) + public async Task> GetSubmodelElementAsync( + [FromRoute] string submodelIdentifier, + [FromRoute] string idShortPath, + CancellationToken cancellationToken, + [FromQuery] Level level = Level.deep, + [FromQuery] Extent extent = Extent.withoutBlobValue) { logger.LogInformation("Get Submodel Element"); - var request = new GetSubmodelElementRequest(submodelIdentifier, idShortPath); + var request = new GetSubmodelElementRequest(submodelIdentifier, idShortPath, level, extent); var response = await submodelRepositoryHandler.GetSubmodelElement(request, cancellationToken).ConfigureAwait(false); return Ok(Jsonization.Serialize.ToJsonObject(response)); } diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/IShellDescriptorService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/IShellDescriptorService.cs index 143996de..21a68c1d 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/IShellDescriptorService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/IShellDescriptorService.cs @@ -1,4 +1,5 @@ using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry; namespace AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRegistry; @@ -7,4 +8,8 @@ public interface IShellDescriptorService Task GetAllShellDescriptorsAsync(int? limit, string? cursor, CancellationToken cancellationToken); Task GetShellDescriptorByIdAsync(string id, CancellationToken cancellationToken); + + Task GetAllSubmodelDescriptorsByAasIdAsync(string aasId, int? limit, string? cursor, CancellationToken cancellationToken); + + Task GetSubmodelDescriptorByAasIdAsync(string aasId, string submodelId, CancellationToken cancellationToken); } diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/ShellDescriptorService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/ShellDescriptorService.cs index fc5d15c3..18714350 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/ShellDescriptorService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRegistry/ShellDescriptorService.cs @@ -1,8 +1,12 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasEnvironment.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; using Microsoft.Extensions.Options; @@ -18,7 +22,9 @@ public class ShellDescriptorService( IPluginDataHandler pluginDataHandler, IPluginManifestConflictHandler pluginManifestConflictHandler, ILogger logger, - IOptions templateManagementConfig) : IShellDescriptorService + IOptions templateManagementConfig, + ISubmodelDescriptorService submodelDescriptorService, + IAasRepositoryService aasRepositoryService) : IShellDescriptorService { private readonly int _concurrentOperationsLimit = templateManagementConfig.Value.AasTemplateRegistry.ConcurrentOperationsLimit; public async Task GetAllShellDescriptorsAsync(int? limit, string? cursor, CancellationToken cancellationToken) @@ -95,6 +101,37 @@ public class ShellDescriptorService( } } + public async Task GetSubmodelDescriptorByAasIdAsync(string aasId, string submodelId, CancellationToken cancellationToken) + { + await aasRepositoryService.ValidateSubmodelBelongsToAasAsync(aasId, submodelId, cancellationToken).ConfigureAwait(false); + + return await submodelDescriptorService.GetSubmodelDescriptorByIdAsync(submodelId, cancellationToken).ConfigureAwait(false); + } + + public async Task GetAllSubmodelDescriptorsByAasIdAsync(string aasId, int? limit, string? cursor, CancellationToken cancellationToken) + { + var submodelRefs = await aasRepositoryService.GetSubmodelRefByIdAsync(aasId, null, null, cancellationToken).ConfigureAwait(false); + + var submodelIds = submodelRefs.Result? + .SelectMany(reference => reference.Keys ?? []) + .Select(key => key.Value) + .Distinct(StringComparer.Ordinal) + .ToList() ?? []; + + var descriptors = (await Task.WhenAll(submodelIds.Select(submodelId => + submodelDescriptorService.GetSubmodelDescriptorByIdAsync(submodelId, cancellationToken))).ConfigureAwait(false)) + .OfType() + .ToList(); + + var (items, pagingMetaData) = PagingExtensions.GetPagedResult(descriptors, descriptor => descriptor.Id, limit, cursor); + + return new SubmodelDescriptors + { + Result = items, + PagingMetaData = pagingMetaData + }; + } + private async Task TryBuildShellDescriptorAsync(ShellDescriptorMetaData shellDescriptorMetadata, CancellationToken cancellationToken) { try diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/AasRepositoryService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/AasRepositoryService.cs index a163e1b7..15deadaf 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/AasRepositoryService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/AasRepositoryService.cs @@ -1,12 +1,14 @@ -using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application; using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure; using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository; using AAS.TwinEngine.DataEngine.DomainModel.AasRegistry; using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config; -using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; using AasCore.Aas3_1; @@ -23,6 +25,7 @@ public class AasRepositoryService( IPluginManifestConflictHandler pluginManifestConflictHandler, IFileContentProvider fileContentProvider, IOptions templateManagementConfig, + ISubmodelRepositoryService submodelRepositoryService, IOptions generalConfig) : IAasRepositoryService { private readonly int _concurrentOperationsLimit = templateManagementConfig.Value.AasTemplateRepository.ConcurrentOperationsLimit; @@ -177,6 +180,48 @@ public async Task GetThumbnailAsync(string aasIdentifier, } } + public async Task GetSubmodelByAasIdAsync(string aasIdentifier, string submodelIdentifier, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken) + { + await ValidateSubmodelBelongsToAasAsync(aasIdentifier, submodelIdentifier, cancellationToken).ConfigureAwait(false); + + return await submodelRepositoryService.GetSubmodelAsync(submodelIdentifier, queryOptions, cancellationToken).ConfigureAwait(false); + } + + public async Task GetAllSubmodelElementsByAasIdAsync(string aasIdentifier, string submodelIdentifier, SubmodelQueryOptions? queryOptions, int? limit, string? cursor, CancellationToken cancellationToken) + { + await ValidateSubmodelBelongsToAasAsync(aasIdentifier, submodelIdentifier, cancellationToken).ConfigureAwait(false); + + return await submodelRepositoryService.GetAllSubmodelElementsAsync(submodelIdentifier, queryOptions, limit, cursor, cancellationToken).ConfigureAwait(false); + } + + public async Task GetSubmodelElementByAasIdAsync(string aasIdentifier, string submodelIdentifier, string idShortPath, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken) + { + await ValidateSubmodelBelongsToAasAsync(aasIdentifier, submodelIdentifier, cancellationToken).ConfigureAwait(false); + + return await submodelRepositoryService.GetSubmodelElementAsync(submodelIdentifier, idShortPath, queryOptions, cancellationToken).ConfigureAwait(false); + } + + public async Task GetFileAttachmentByAasIdAsync(string aasIdentifier, string submodelId, string idShortPath, CancellationToken cancellationToken) + { + await ValidateSubmodelBelongsToAasAsync(aasIdentifier, submodelId, cancellationToken).ConfigureAwait(false); + + return await submodelRepositoryService.GetFileAttachmentAsync(submodelId, idShortPath, cancellationToken).ConfigureAwait(false); + } + + public async Task ValidateSubmodelBelongsToAasAsync(string aasIdentifier, string submodelIdentifier, CancellationToken cancellationToken) + { + var submodelRefs = await GetSubmodelRefByIdAsync(aasIdentifier, null, null, cancellationToken).ConfigureAwait(false); + + var submodelExists = submodelRefs.Result?.SelectMany(r => r.Keys ?? []) + .Any(k => string.Equals(k.Value, submodelIdentifier, StringComparison.Ordinal)) ?? false; + + if (!submodelExists) + { + logger.LogError("Submodel {SubmodelId} not referenced by AAS {AasId}", submodelIdentifier, aasIdentifier); + throw new SubmodelNotFoundException(submodelIdentifier); + } + } + private static IAssetInformation FillOutAssetInformation(IAssetInformation template, AssetData pluginData) { if (template is null) diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/IAasRepositoryService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/IAasRepositoryService.cs index 36938f48..a0b012a2 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/IAasRepositoryService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/AasRepository/IAasRepositoryService.cs @@ -1,5 +1,6 @@ -using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; +using AAS.TwinEngine.DataEngine.DomainModel.AasRepository; using AAS.TwinEngine.DataEngine.DomainModel.Shared; +using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository; using AasCore.Aas3_1; @@ -15,5 +16,15 @@ public interface IAasRepositoryService Task GetSubmodelRefByIdAsync(string aasIdentifier, int? limit, string? cursor, CancellationToken cancellationToken); + Task ValidateSubmodelBelongsToAasAsync(string aasIdentifier, string submodelIdentifier, CancellationToken cancellationToken); + + Task GetSubmodelByAasIdAsync(string aasIdentifier, string submodelIdentifier, SubmodelQueryOptions queryOptions, CancellationToken cancellationToken); + + Task GetAllSubmodelElementsByAasIdAsync(string aasIdentifier, string submodelIdentifier, SubmodelQueryOptions queryOptions, int? limit, string? cursor, CancellationToken cancellationToken); + + Task GetSubmodelElementByAasIdAsync(string aasIdentifier, string submodelIdentifier, string idShortPath, SubmodelQueryOptions queryOptions, CancellationToken cancellationToken); + + Task GetFileAttachmentByAasIdAsync(string aasIdentifier, string submodelId, string idShortPath, CancellationToken cancellationToken); + Task GetThumbnailAsync(string aasIdentifier, CancellationToken cancellationToken); } diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelRepositoryService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelRepositoryService.cs index e6e59720..d53145f6 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelRepositoryService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelRepositoryService.cs @@ -9,7 +9,7 @@ public interface ISubmodelRepositoryService { Task GetSubmodelAsync(string submodelId, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken); - Task GetSubmodelElementAsync(string submodelId, string idShortPath, CancellationToken cancellationToken); + Task GetSubmodelElementAsync(string submodelId, string idShortPath, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken); Task GetAllSubmodelsAsync( SubmodelSearchFilter? filter, diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelTemplateService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelTemplateService.cs index c4938323..fa6f2855 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelTemplateService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/ISubmodelTemplateService.cs @@ -9,7 +9,7 @@ public interface ISubmodelTemplateService { Task GetSubmodelTemplateAsync(string submodelId, CancellationToken cancellationToken); - Task GetSubmodelTemplateAsync(string submodelId, string idShortPath, CancellationToken cancellationToken); + Task GetSubmodelTemplateAsync(string submodelId, string idShortPath, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken); Task GetFilteredSubmodelTemplateAsync(string submodelId, string filteredTemplateId, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken); diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandler.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandler.cs index 34f00255..acfbba9a 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandler.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SemanticId/ElementHandlers/BlobHandler.cs @@ -17,9 +17,9 @@ public class BlobHandler(ISemanticIdResolver semanticIdResolver) : ISubmodelElem public void FillOut(ISubmodelElement element, SemanticTreeNode values, Action, SemanticTreeNode, bool> fillOutChildren) { - if (values is SemanticLeafNode leafValueNode) + if (values is SemanticLeafNode leafValueNode && element is Blob { Value: not null } blob) { - ((Blob)element).Value = Convert.FromBase64String(leafValueNode.Value); + blob.Value = Convert.FromBase64String(leafValueNode.Value); } } } diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryService.cs index 1853bcb6..cfa30d7e 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelRepositoryService.cs @@ -14,8 +14,6 @@ using Microsoft.Extensions.Options; -using Serilog.Core; - using File = AasCore.Aas3_1.File; using UnauthorizedAccessException = AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure.UnauthorizedAccessException; @@ -54,11 +52,11 @@ public async Task GetSubmodelAsync(string submodelId, SubmodelQueryOp }, ex => new SubmodelNotFoundException(ex)).ConfigureAwait(false); } - public async Task GetSubmodelElementAsync(string submodelId, string idShortPath, CancellationToken cancellationToken) + public async Task GetSubmodelElementAsync(string submodelId, string idShortPath, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken) { return await ExecuteWithExceptionHandlingAsync(async () => { - var reducedSubmodelTemplate = await submodelTemplateService.GetSubmodelTemplateAsync(submodelId, idShortPath, cancellationToken).ConfigureAwait(false); + var reducedSubmodelTemplate = await submodelTemplateService.GetSubmodelTemplateAsync(submodelId, idShortPath, queryOptions, cancellationToken).ConfigureAwait(false); var submodelWithValues = await BuildSubmodelWithValuesAsync(reducedSubmodelTemplate, submodelId, cancellationToken).ConfigureAwait(false); @@ -66,7 +64,6 @@ public async Task GetSubmodelElementAsync(string submodelId, s }, ex => new SubmodelElementNotFoundException(ex)).ConfigureAwait(false); } - public async Task GetAllSubmodelsAsync(SubmodelSearchFilter? filter, SubmodelQueryOptions? queryOptions, int? limit, string? cursor, CancellationToken cancellationToken) { return await ExecuteWithExceptionHandlingAsync(async () => @@ -256,7 +253,7 @@ public async Task GetFileAttachmentAsync(string submodelId private async Task GetFileElementAsync(string submodelId, string idShortPath, CancellationToken cancellationToken) { - var element = await GetSubmodelElementAsync(submodelId, idShortPath, cancellationToken); + var element = await GetSubmodelElementAsync(submodelId, idShortPath, null, cancellationToken); return GetFileElement(element, idShortPath); } diff --git a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateService.cs b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateService.cs index d79f3287..8f1ba0fa 100644 --- a/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateService.cs +++ b/source/AAS.TwinEngine.DataEngine/ApplicationLogic/Services/SubmodelRepository/SubmodelTemplateService.cs @@ -50,7 +50,7 @@ public async Task GetSubmodelTemplateAsync(string submodelId, Cancell } } - public async Task GetSubmodelTemplateAsync(string submodelId, string idShortPath, CancellationToken cancellationToken) + public async Task GetSubmodelTemplateAsync(string submodelId, string idShortPath, SubmodelQueryOptions? queryOptions, CancellationToken cancellationToken) { if (!string.IsNullOrWhiteSpace(idShortPath)) { @@ -59,7 +59,7 @@ public async Task GetSubmodelTemplateAsync(string submodelId, string ValidateSubmodelId(submodelId); var templateId = _submodelTemplateMappingProvider.GetTemplateId(submodelId); - var submodel = await _templateProvider.GetFilteredSubmodelTemplateAsync(templateId!, null, cancellationToken).ConfigureAwait(false); + var submodel = await _templateProvider.GetFilteredSubmodelTemplateAsync(templateId, queryOptions, cancellationToken).ConfigureAwait(false); return BuildSubmodel(submodel, idShortPath); } diff --git a/source/AAS.TwinEngine.DataEngine/ServiceConfiguration/InfrastructureDependencyInjectionExtensions.cs b/source/AAS.TwinEngine.DataEngine/ServiceConfiguration/InfrastructureDependencyInjectionExtensions.cs index 4575d289..0f4e7379 100644 --- a/source/AAS.TwinEngine.DataEngine/ServiceConfiguration/InfrastructureDependencyInjectionExtensions.cs +++ b/source/AAS.TwinEngine.DataEngine/ServiceConfiguration/InfrastructureDependencyInjectionExtensions.cs @@ -2,8 +2,8 @@ using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Helper; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers; -using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry.Providers; using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers; +using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRegistry.Providers; using AAS.TwinEngine.DataEngine.Infrastructure.Configuration.LegacyV1; using AAS.TwinEngine.DataEngine.Infrastructure.Http.Authorization.Headers; using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients; diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests.csproj b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests.csproj index c43e5902..15f8f009 100644 --- a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests.csproj +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests.csproj @@ -33,6 +33,12 @@ Always + + Always + + + Always + Always @@ -63,6 +69,15 @@ Always + + Always + + + Always + + + Always + Always diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/AasRegistryTests.cs b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/AasRegistryTests.cs index eab3827c..46fb0114 100644 --- a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/AasRegistryTests.cs +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/AasRegistryTests.cs @@ -84,4 +84,71 @@ public async Task GetShellDescriptorById_ShouldReturnSuccess_ContentAsExpected() await CompareJsonAsync(actualDoc, Path.Combine(Directory.GetCurrentDirectory(), "AasRegistry", "TestData", "GetShellDescriptorById_Expected.json")); } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasId_ShouldReturnSuccess_ContentAsExpected() + { + // Arrange + var url = $"/shell-descriptors/{AasIdentifier}/submodel-descriptors"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + AssertSuccessResponse(response); + + var content = await response.TextAsync(); + Assert.False(string.IsNullOrEmpty(content)); + + var actualDoc = JsonDocument.Parse(content); + Assert.NotNull(actualDoc); + + await CompareJsonAsync(actualDoc, Path.Combine(Directory.GetCurrentDirectory(), "AasRegistry", "TestData", "GetAllSubmodelDescriptorsByAasId_Expected.json")); + } + + [Fact] + public async Task GetAllSubmodelDescriptorsByAasId_WithPagination() + { + // Arrange + var urlLimit2 = $"/shell-descriptors/{AasIdentifier}/submodel-descriptors?limit=2"; + var urlLimit3 = $"/shell-descriptors/{AasIdentifier}/submodel-descriptors?limit=3"; + + // Act + var responseLimit2 = await ApiContext.GetAsync(urlLimit2); + var responseLimit3 = await ApiContext.GetAsync(urlLimit3); + + // Assert + AssertSuccessResponse(responseLimit2); + AssertSuccessResponse(responseLimit3); + + var jsonLimit2 = JsonDocument.Parse(await responseLimit2.TextAsync()); + var jsonLimit3 = JsonDocument.Parse(await responseLimit3.TextAsync()); + + var resultLimit2 = jsonLimit2.RootElement.GetProperty("result"); + var resultLimit3 = jsonLimit3.RootElement.GetProperty("result"); + + Assert.Equal(resultLimit2.GetArrayLength() + 1, resultLimit3.GetArrayLength()); + } + + [Fact] + public async Task GetSubmodelDescriptorByAasId_ShouldReturnSuccess_ContentAsExpected() + { + // Arrange + var url = + $"/shell-descriptors/{AasIdentifier}/submodel-descriptors/{SubmodelIdentifierHandoverDocumentation}"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + AssertSuccessResponse(response); + + var content = await response.TextAsync(); + Assert.False(string.IsNullOrEmpty(content)); + + var actualDoc = JsonDocument.Parse(content); + Assert.NotNull(actualDoc); + + await CompareJsonAsync(actualDoc, Path.Combine(Directory.GetCurrentDirectory(), "AasRegistry", "TestData", "GetSubmodelDescriptorByAasId_Expected.json")); + } } diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetAllSubmodelDescriptorsByAasId_Expected.json b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetAllSubmodelDescriptorsByAasId_Expected.json new file mode 100644 index 00000000..a1f0e1a7 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetAllSubmodelDescriptorsByAasId_Expected.json @@ -0,0 +1,122 @@ +{ + "paging_metadata": { + "cursor": null + }, + "result": [ + { + "description": [ + { + "language": "en", + "text": "The Submodel HierarchicalStructures identified by its semanticId. The Submodel idShort can be picked freely." + } + ], + "displayName": null, + "extensions": null, + "administration": null, + "idShort": "CustomSubmodel", + "id": "https://mm-software.com/submodel/000-001/CustomSubmodel", + "semanticId": { + "type": "ExternalReference", + "referredSemanticId": null, + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/CustomSubmodel/Submodel/Template/0/1" + } + ] + }, + "supplementalSemanticId": null, + "endpoints": [ + { + "interface": "SUBMODEL-3.0", + "protocolInformation": { + "href": "http://localhost:8080/submodels/aHR0cHM6Ly9tbS1zb2Z0d2FyZS5jb20vc3VibW9kZWwvMDAwLTAwMS9DdXN0b21TdWJtb2RlbA", + "endpointProtocol": "http", + "endpointProtocolVersion": null, + "subprotocol": null, + "subprotocolBody": null, + "subprotocolBodyEncoding": null, + "securityAttributes": null + } + } + ] + }, + { + "description": null, + "displayName": null, + "extensions": null, + "administration": null, + "idShort": "ContactInformations", + "id": "https://mm-software.com/submodel/000-001/ContactInformation", + "semanticId": { + "type": "ModelReference", + "referredSemanticId": null, + "keys": [ + { + "type": "Submodel", + "value": "https://admin-shell.io/zvei/nameplate/1/0/ContactInformations" + } + ] + }, + "supplementalSemanticId": null, + "endpoints": [ + { + "interface": "SUBMODEL-3.0", + "protocolInformation": { + "href": "http://localhost:8080/submodels/aHR0cHM6Ly9tbS1zb2Z0d2FyZS5jb20vc3VibW9kZWwvMDAwLTAwMS9Db250YWN0SW5mb3JtYXRpb24", + "endpointProtocol": "http", + "endpointProtocolVersion": null, + "subprotocol": null, + "subprotocolBody": null, + "subprotocolBodyEncoding": null, + "securityAttributes": null + } + } + ] + }, + { + "description": [ + { + "language": "en", + "text": "The Submodel defines a set meta data for the handover of documentation from the manufacturer to the operator for industrial equipment" + } + ], + "displayName": null, + "extensions": null, + "administration": { + "embeddedDataSpecifications": null, + "version": "2", + "revision": "0", + "creator": null, + "templateId": "https://admin-shell.io/idta-02004-2-0" + }, + "idShort": "HandoverDocumentation", + "id": "https://mm-software.com/submodel/000-001/HandoverDocumentation", + "semanticId": { + "type": "ModelReference", + "referredSemanticId": null, + "keys": [ + { + "type": "Submodel", + "value": "0173-1#01-AHF578#003" + } + ] + }, + "supplementalSemanticId": null, + "endpoints": [ + { + "interface": "SUBMODEL-3.0", + "protocolInformation": { + "href": "http://localhost:8080/submodels/aHR0cHM6Ly9tbS1zb2Z0d2FyZS5jb20vc3VibW9kZWwvMDAwLTAwMS9IYW5kb3ZlckRvY3VtZW50YXRpb24", + "endpointProtocol": "http", + "endpointProtocolVersion": null, + "subprotocol": null, + "subprotocolBody": null, + "subprotocolBodyEncoding": null, + "securityAttributes": null + } + } + ] + } + ] +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetSubmodelDescriptorByAasId_Expected.json b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetSubmodelDescriptorByAasId_Expected.json new file mode 100644 index 00000000..8695ba29 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRegistry/TestData/GetSubmodelDescriptorByAasId_Expected.json @@ -0,0 +1,44 @@ +{ + "description": [ + { + "language": "en", + "text": "The Submodel defines a set meta data for the handover of documentation from the manufacturer to the operator for industrial equipment" + } + ], + "displayName": null, + "extensions": null, + "administration": { + "embeddedDataSpecifications": null, + "version": "2", + "revision": "0", + "creator": null, + "templateId": "https://admin-shell.io/idta-02004-2-0" + }, + "idShort": "HandoverDocumentation", + "id": "https://mm-software.com/submodel/000-001/HandoverDocumentation", + "semanticId": { + "type": "ModelReference", + "referredSemanticId": null, + "keys": [ + { + "type": "Submodel", + "value": "0173-1#01-AHF578#003" + } + ] + }, + "supplementalSemanticId": null, + "endpoints": [ + { + "interface": "SUBMODEL-3.0", + "protocolInformation": { + "href": "http://localhost:8080/submodels/aHR0cHM6Ly9tbS1zb2Z0d2FyZS5jb20vc3VibW9kZWwvMDAwLTAwMS9IYW5kb3ZlckRvY3VtZW50YXRpb24", + "endpointProtocol": "http", + "endpointProtocolVersion": null, + "subprotocol": null, + "subprotocolBody": null, + "subprotocolBodyEncoding": null, + "securityAttributes": null + } + } + ] +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/AasRepositoryTests.cs b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/AasRepositoryTests.cs index 5ec2ed97..3e1946e9 100644 --- a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/AasRepositoryTests.cs +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/AasRepositoryTests.cs @@ -209,7 +209,6 @@ public async Task GetShellById_ShouldReturnSuccess_ContentAsExpected() var content = await response.TextAsync(); Assert.False(string.IsNullOrEmpty(content)); - // Verify it's valid JSON var json = JsonDocument.Parse(content); Assert.NotNull(json); @@ -272,6 +271,177 @@ public async Task GetSubmodelRefById_ShouldReturnSuccess_ContentAsExpected() await CompareJsonAsync(json, Path.Combine(Directory.GetCurrentDirectory(), "AasRepository", "TestData", "GetSubmodelRefById_Expected.json")); } + [Fact] + public async Task GetSubmodelByAasId_ShouldReturnSuccess_ContentAsExpected() + { + // Arrange + var url = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierCustomSubmodel}"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + AssertSuccessResponse(response); + + var content = await response.TextAsync(); + Assert.False(string.IsNullOrEmpty(content)); + + var json = JsonDocument.Parse(content); + Assert.NotNull(json); + + await CompareJsonAsync(json, Path.Combine(Directory.GetCurrentDirectory(), "AasRepository", "TestData", "GetSubmodelByAasId_Expected.json")); + } + + [Fact] + public async Task GetSubmodelByAasId_WithLevelAndExtent_ShouldReturnSuccess() + { + // Arrange + var url = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierCustomSubmodel}" + "?level=deep&extent=withoutBlobValue"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + AssertSuccessResponse(response); + + var content = await response.TextAsync(); + Assert.False(string.IsNullOrEmpty(content)); + + var json = JsonDocument.Parse(content); + Assert.NotNull(json); + + await CompareJsonAsync(json, Path.Combine(Directory.GetCurrentDirectory(), "AasRepository", "TestData", "GetSubmodelByAasId_Expected.json")); + } + + [Fact] + public async Task GetSubmodelByAasId_WithInvalidSubmodelId_ShouldReturnNotFound() + { + // Arrange + var url = $"/shells/{AasIdentifier}/submodels/invalid//submodel"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + Assert.Equal(404, response.Status); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasId_ShouldReturnSuccess_ContentAsExpected() + { + // Arrange + var url = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + AssertSuccessResponse(response); + + var content = await response.TextAsync(); + Assert.False(string.IsNullOrEmpty(content)); + + var json = JsonDocument.Parse(content); + Assert.NotNull(json); + + await CompareJsonAsync(json, Path.Combine(Directory.GetCurrentDirectory(), "AasRepository", "TestData", "GetAllSubmodelElementsByAasId_Expected.json")); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasId_WithPagination() + { + // Arrange + var urlLimit2 = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements?limit=1"; + + var urlLimit3 = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements?limit=2"; + + // Act + var responseLimit2 = await ApiContext.GetAsync(urlLimit2); + var responseLimit3 = await ApiContext.GetAsync(urlLimit3); + + // Assert + AssertSuccessResponse(responseLimit2); + AssertSuccessResponse(responseLimit3); + + var jsonLimit2 = JsonDocument.Parse(await responseLimit2.TextAsync()); + var jsonLimit3 = JsonDocument.Parse(await responseLimit3.TextAsync()); + + var resultLimit2 = jsonLimit2.RootElement.GetProperty("result"); + var resultLimit3 = jsonLimit3.RootElement.GetProperty("result"); + + Assert.Equal(resultLimit2.GetArrayLength() + 1, resultLimit3.GetArrayLength()); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasId_WithCursor_ShouldReturnNextPage() + { + // Arrange + var firstPageUrl = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements?limit=1"; + + // Act + var firstResponse = await ApiContext.GetAsync(firstPageUrl); + + // Assert first page + AssertSuccessResponse(firstResponse); + + var firstRoot = await ParseResponseRootAsync(firstResponse); + var firstResult = firstRoot.GetProperty("result"); + + Assert.Single(firstResult.EnumerateArray()); + + var cursor = firstRoot.GetProperty("paging_metadata").GetProperty("cursor").GetString(); + + Assert.False(string.IsNullOrWhiteSpace(cursor)); + + var firstIdShort = firstResult[0].GetProperty("idShort").GetString(); + + // Act second page + var secondResponse = await ApiContext.GetAsync($"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements?limit=1&cursor={cursor}"); + + // Assert second page + AssertSuccessResponse(secondResponse); + + var secondRoot = await ParseResponseRootAsync(secondResponse); + var secondResult = secondRoot.GetProperty("result"); + + Assert.Single(secondResult.EnumerateArray()); + + var secondIdShort = secondResult[0].GetProperty("idShort").GetString(); + + Assert.NotEqual(firstIdShort, secondIdShort); + + var json = JsonDocument.Parse(secondRoot.GetRawText()); + + await CompareJsonAsync(json, Path.Combine(Directory.GetCurrentDirectory(), "AasRepository", "TestData", "GetAllSubmodelElementsByAasId_WithCursor_Expected.json")); + } + + [Fact] + public async Task GetAllSubmodelElementsByAasId_WithInvalidLimit_ShouldReturnBadRequest() + { + // Arrange + var url = + $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierHandoverDocumentation}/submodel-elements?limit=0"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + Assert.Equal(400, response.Status); + } + + [Fact] + public async Task GetFileByPathByAasId_WithUnknownElement_ShouldReturnNotFound() + { + // Arrange + var url = $"/shells/{AasIdentifier}/submodels/{SubmodelIdentifierContact}/submodel-elements/ContactName/attachment"; + + // Act + var response = await ApiContext.GetAsync(url); + + // Assert + Assert.Equal(404, response.Status); + } + private static async Task ParseResponseRootAsync(Microsoft.Playwright.IAPIResponse response) { var content = await response.TextAsync(); diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_Expected.json b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_Expected.json new file mode 100644 index 00000000..54f840f4 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_Expected.json @@ -0,0 +1,6935 @@ +{ + "paging_metadata": { + "cursor": null + }, + "result": [ + { + "idShort": "Documents", + "displayName": [ + { + "language": "en", + "text": "Documents (handover documentation)" + }, + { + "language": "de", + "text": "Dokumente (Übergabedokumentation)" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI500-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003/0173-1#01-AHF579#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003/0173-1#01-AHF579#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003~0/0173-1#01-AHF579#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI500-003/0173-1-01-AHF579-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "DocumentIds", + "displayName": [ + { + "language": "en", + "text": "Document identifyers" + }, + { + "language": "de", + "text": "Dokumentidentifikatoren" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document identificator" + }, + { + "language": "de", + "text": "Dokumentidentifikator" + } + ], + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003~0/0173-1#01-AHF580#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003/0173-1-01-AHF580-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "DocumentDomainId", + "displayName": [ + { + "language": "en", + "text": "document domain identificator" + }, + { + "language": "de", + "text": "Document Domain Identifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH994#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH994-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "https://domain.com/..." + } + ], + "valueType": "xs:string", + "value": "a3f1c2b4-9d81-4e5a-b6f2-01ac9e11d001", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIdentifier", + "displayName": [ + { + "language": "en", + "text": "Document Identifyer" + }, + { + "language": "de", + "text": "Dokumentennummer" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAO099#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAO099-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "XF90-884" + } + ], + "valueType": "xs:string", + "value": "DOC2025A001", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIsPrimary", + "displayName": [ + { + "language": "en", + "text": "Document is primary" + }, + { + "language": "de", + "text": "Dokument ist primär" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH995#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH995-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "true" + } + ], + "valueType": "xs:boolean", + "value": "True", + "modelType": "Property" + } + ], + "modelType": "SubmodelElementCollection" + }, + { + "displayName": [ + { + "language": "en", + "text": "Document identificator" + }, + { + "language": "de", + "text": "Dokumentidentifikator" + } + ], + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003~0/0173-1#01-AHF580#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003/0173-1-01-AHF580-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "DocumentDomainId", + "displayName": [ + { + "language": "en", + "text": "document domain identificator" + }, + { + "language": "de", + "text": "Document Domain Identifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH994#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH994-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "https://domain.com/..." + } + ], + "valueType": "xs:string", + "value": "b7e92d10-3c45-4a8f-9f21-02bc9e11d002", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIdentifier", + "displayName": [ + { + "language": "en", + "text": "Document Identifyer" + }, + { + "language": "de", + "text": "Dokumentennummer" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAO099#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAO099-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "XF90-884" + } + ], + "valueType": "xs:string", + "value": "CERTX94B21", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIsPrimary", + "displayName": [ + { + "language": "en", + "text": "Document is primary" + }, + { + "language": "de", + "text": "Dokument ist primär" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH995#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH995-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "true" + } + ], + "valueType": "xs:boolean", + "value": "False", + "modelType": "Property" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentClassifications", + "displayName": [ + { + "language": "en", + "text": "Document classifications" + }, + { + "language": "de", + "text": "Dokumentklassifikationen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document classification" + }, + { + "language": "de", + "text": "Dokumentklassifikation" + } + ], + "description": [ + { + "language": "en", + "text": "Set of information for describing the classification of the Document according to a ClassificationSystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003~0/0173-1#01-AHF581#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003/0173-1-01-AHF581-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "ClassId", + "displayName": [ + { + "language": "en", + "text": "Class identificator" + }, + { + "language": "de", + "text": "Klassenidentifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH996#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH996-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "03-02" + } + ], + "valueType": "xs:string", + "value": "CLS-001", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassificationSystem", + "displayName": [ + { + "language": "en", + "text": "Classification system" + }, + { + "language": "de", + "text": "Klassifizierungssystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH997#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH997-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "VDI2770:2020" + } + ], + "valueType": "xs:string", + "value": "IEC-61360", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassName", + "displayName": [ + { + "language": "en", + "text": "Class Name" + }, + { + "language": "de", + "text": "Klassenname" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABJ219#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABJ219-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Operation@en" + } + ], + "modelType": "MultiLanguageProperty" + } + ], + "modelType": "SubmodelElementCollection" + }, + { + "displayName": [ + { + "language": "en", + "text": "Document classification" + }, + { + "language": "de", + "text": "Dokumentklassifikation" + } + ], + "description": [ + { + "language": "en", + "text": "Set of information for describing the classification of the Document according to a ClassificationSystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003~0/0173-1#01-AHF581#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003/0173-1-01-AHF581-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "ClassId", + "displayName": [ + { + "language": "en", + "text": "Class identificator" + }, + { + "language": "de", + "text": "Klassenidentifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH996#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH996-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "03-02" + } + ], + "valueType": "xs:string", + "value": "CLS-002", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassificationSystem", + "displayName": [ + { + "language": "en", + "text": "Classification system" + }, + { + "language": "de", + "text": "Klassifizierungssystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH997#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH997-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "VDI2770:2020" + } + ], + "valueType": "xs:string", + "value": "ISO-13584", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassName", + "displayName": [ + { + "language": "en", + "text": "Class Name" + }, + { + "language": "de", + "text": "Klassenname" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABJ219#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABJ219-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Operation@en" + } + ], + "modelType": "MultiLanguageProperty" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentVersions", + "displayName": [ + { + "language": "en", + "text": "Document versions" + }, + { + "language": "de", + "text": "Dokumentenversionen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Document version" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003~0/0173-1#01-AHF582#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003/0173-1-01-AHF582-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "Language", + "displayName": [ + { + "language": "en", + "text": "Language" + }, + { + "language": "de", + "text": "Sprache" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN468-008" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "Property", + "valueTypeListElement": "xs:string", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "en (English)" + }, + { + "language": "de", + "text": "en (Englisch)" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "en" + } + ], + "valueType": "xs:string", + "value": "en", + "valueId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#07-AAS045#003" + } + ] + }, + "modelType": "Property" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DigitalFiles", + "displayName": [ + { + "language": "en", + "text": "Digital files" + }, + { + "language": "de", + "text": "Digitale Dateien" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK126-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "File", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Name of the specific digital file@en" + }, + { + "language": "de", + "text": "Name der spezifischen digitalen Datei@de" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.PDF" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "DigitalFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.pdf", + "contentType": "application/pdf", + "modelType": "File" + } + ], + "modelType": "SubmodelElementList" + }, + { + "category": "PARAMETER", + "idShort": "Version", + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Dokumentenversion" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAP003#005" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAP003-005" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "V1.2" + } + ], + "valueType": "xs:string", + "value": "1", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusSetDate", + "displayName": [ + { + "language": "en", + "text": "Document status set date" + }, + { + "language": "de", + "text": "Datum der Einstellung des Dokumentenstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI000#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI000-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "2020-02-06" + } + ], + "valueType": "xs:date", + "value": "2023-01-01", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusValue", + "displayName": [ + { + "language": "en", + "text": "Document status" + }, + { + "language": "de", + "text": "Dokumentstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI001#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI001-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Released" + } + ], + "valueType": "xs:string", + "value": "Released", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationShortName", + "displayName": [ + { + "language": "en", + "text": "Organization short name" + }, + { + "language": "de", + "text": "Kurzname der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI002-003" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company" + } + ], + "valueType": "xs:string", + "value": "M&M", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationOfficialName", + "displayName": [ + { + "language": "en", + "text": "Organization official name" + }, + { + "language": "de", + "text": "Offizieller Name der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI004#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI004-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company Ltd." + } + ], + "valueType": "xs:string", + "value": "M&M Germany", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "Title", + "displayName": [ + { + "language": "en", + "text": "Document title" + }, + { + "language": "de", + "text": "Dokumententitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABG940#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABG940-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary title@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Subtitle", + "displayName": [ + { + "language": "en", + "text": "Subtitle" + }, + { + "language": "de", + "text": "Untertitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH998#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH998-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary subtitle@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Description", + "displayName": [ + { + "language": "en", + "text": "Document description" + }, + { + "language": "de", + "text": "Dokumentenbeschreibung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN466#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN466-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Abstract@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "KeyWords", + "displayName": [ + { + "language": "en", + "text": "Keywords" + }, + { + "language": "de", + "text": "Stichworte" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH999#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH999-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary keywords@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "PreviewFile", + "displayName": [ + { + "language": "en", + "text": "Preview file" + }, + { + "language": "de", + "text": "Vorschaudatei" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK127#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK127-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.jpg" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "PreviewFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.jpg", + "contentType": "image/jpeg", + "modelType": "File" + } + ], + "modelType": "SubmodelElementCollection" + }, + { + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Document version" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003~0/0173-1#01-AHF582#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003/0173-1-01-AHF582-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "Language", + "displayName": [ + { + "language": "en", + "text": "Language" + }, + { + "language": "de", + "text": "Sprache" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN468-008" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "Property", + "valueTypeListElement": "xs:string", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "en (English)" + }, + { + "language": "de", + "text": "en (Englisch)" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "en" + } + ], + "valueType": "xs:string", + "value": "de", + "valueId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#07-AAS045#003" + } + ] + }, + "modelType": "Property" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DigitalFiles", + "displayName": [ + { + "language": "en", + "text": "Digital files" + }, + { + "language": "de", + "text": "Digitale Dateien" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK126-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "File", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Name of the specific digital file@en" + }, + { + "language": "de", + "text": "Name der spezifischen digitalen Datei@de" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.PDF" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "DigitalFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.pdf", + "contentType": "application/pdf", + "modelType": "File" + } + ], + "modelType": "SubmodelElementList" + }, + { + "category": "PARAMETER", + "idShort": "Version", + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Dokumentenversion" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAP003#005" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAP003-005" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "V1.2" + } + ], + "valueType": "xs:string", + "value": "1.1", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusSetDate", + "displayName": [ + { + "language": "en", + "text": "Document status set date" + }, + { + "language": "de", + "text": "Datum der Einstellung des Dokumentenstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI000#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI000-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "2020-02-06" + } + ], + "valueType": "xs:date", + "value": "2024-05-05", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusValue", + "displayName": [ + { + "language": "en", + "text": "Document status" + }, + { + "language": "de", + "text": "Dokumentstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI001#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI001-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Released" + } + ], + "valueType": "xs:string", + "value": "InReview", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationShortName", + "displayName": [ + { + "language": "en", + "text": "Organization short name" + }, + { + "language": "de", + "text": "Kurzname der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI002-003" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company" + } + ], + "valueType": "xs:string", + "value": "M&M", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationOfficialName", + "displayName": [ + { + "language": "en", + "text": "Organization official name" + }, + { + "language": "de", + "text": "Offizieller Name der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI004#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI004-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company Ltd." + } + ], + "valueType": "xs:string", + "value": "M&M India", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "Title", + "displayName": [ + { + "language": "en", + "text": "Document title" + }, + { + "language": "de", + "text": "Dokumententitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABG940#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABG940-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary title@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Subtitle", + "displayName": [ + { + "language": "en", + "text": "Subtitle" + }, + { + "language": "de", + "text": "Untertitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH998#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH998-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary subtitle@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Description", + "displayName": [ + { + "language": "en", + "text": "Document description" + }, + { + "language": "de", + "text": "Dokumentenbeschreibung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN466#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN466-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Abstract@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "KeyWords", + "displayName": [ + { + "language": "en", + "text": "Keywords" + }, + { + "language": "de", + "text": "Stichworte" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH999#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH999-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary keywords@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "PreviewFile", + "displayName": [ + { + "language": "en", + "text": "Preview file" + }, + { + "language": "de", + "text": "Vorschaudatei" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK127#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK127-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.jpg" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "PreviewFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.jpg", + "contentType": "image/jpeg", + "modelType": "File" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + } + ], + "modelType": "SubmodelElementCollection" + }, + { + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003/0173-1#01-AHF579#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003~0/0173-1#01-AHF579#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI500-003/0173-1-01-AHF579-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "DocumentIds", + "displayName": [ + { + "language": "en", + "text": "Document identifyers" + }, + { + "language": "de", + "text": "Dokumentidentifikatoren" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document identificator" + }, + { + "language": "de", + "text": "Dokumentidentifikator" + } + ], + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003~0/0173-1#01-AHF580#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003/0173-1-01-AHF580-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "DocumentDomainId", + "displayName": [ + { + "language": "en", + "text": "document domain identificator" + }, + { + "language": "de", + "text": "Document Domain Identifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH994#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH994-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "https://domain.com/..." + } + ], + "valueType": "xs:string", + "value": "c81a4f22-6d91-43bb-a812-03cd9e11d003", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIdentifier", + "displayName": [ + { + "language": "en", + "text": "Document Identifyer" + }, + { + "language": "de", + "text": "Dokumentennummer" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAO099#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAO099-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "XF90-884" + } + ], + "valueType": "xs:string", + "value": "MANUAL8F2Q7", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIsPrimary", + "displayName": [ + { + "language": "en", + "text": "Document is primary" + }, + { + "language": "de", + "text": "Dokument ist primär" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH995#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH995-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "true" + } + ], + "valueType": "xs:boolean", + "value": "True", + "modelType": "Property" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentClassifications", + "displayName": [ + { + "language": "en", + "text": "Document classifications" + }, + { + "language": "de", + "text": "Dokumentklassifikationen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document classification" + }, + { + "language": "de", + "text": "Dokumentklassifikation" + } + ], + "description": [ + { + "language": "en", + "text": "Set of information for describing the classification of the Document according to a ClassificationSystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003~0/0173-1#01-AHF581#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003/0173-1-01-AHF581-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "ClassId", + "displayName": [ + { + "language": "en", + "text": "Class identificator" + }, + { + "language": "de", + "text": "Klassenidentifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH996#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH996-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "03-02" + } + ], + "valueType": "xs:string", + "value": "CLS-003", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassificationSystem", + "displayName": [ + { + "language": "en", + "text": "Classification system" + }, + { + "language": "de", + "text": "Klassifizierungssystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH997#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH997-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "VDI2770:2020" + } + ], + "valueType": "xs:string", + "value": "ECLASS-13.0", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassName", + "displayName": [ + { + "language": "en", + "text": "Class Name" + }, + { + "language": "de", + "text": "Klassenname" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABJ219#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABJ219-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Operation@en" + } + ], + "modelType": "MultiLanguageProperty" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentVersions", + "displayName": [ + { + "language": "en", + "text": "Document versions" + }, + { + "language": "de", + "text": "Dokumentenversionen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Document version" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003~0/0173-1#01-AHF582#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003/0173-1-01-AHF582-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "Language", + "displayName": [ + { + "language": "en", + "text": "Language" + }, + { + "language": "de", + "text": "Sprache" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN468-008" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "Property", + "valueTypeListElement": "xs:string", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "en (English)" + }, + { + "language": "de", + "text": "en (Englisch)" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "en" + } + ], + "valueType": "xs:string", + "value": "en", + "valueId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#07-AAS045#003" + } + ] + }, + "modelType": "Property" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DigitalFiles", + "displayName": [ + { + "language": "en", + "text": "Digital files" + }, + { + "language": "de", + "text": "Digitale Dateien" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK126-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "File", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Name of the specific digital file@en" + }, + { + "language": "de", + "text": "Name der spezifischen digitalen Datei@de" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.PDF" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "DigitalFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.pdf", + "contentType": "application/pdf", + "modelType": "File" + } + ], + "modelType": "SubmodelElementList" + }, + { + "category": "PARAMETER", + "idShort": "Version", + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Dokumentenversion" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAP003#005" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAP003-005" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "V1.2" + } + ], + "valueType": "xs:string", + "value": "2.1", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusSetDate", + "displayName": [ + { + "language": "en", + "text": "Document status set date" + }, + { + "language": "de", + "text": "Datum der Einstellung des Dokumentenstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI000#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI000-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "2020-02-06" + } + ], + "valueType": "xs:date", + "value": "2026-01-01", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusValue", + "displayName": [ + { + "language": "en", + "text": "Document status" + }, + { + "language": "de", + "text": "Dokumentstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI001#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI001-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Released" + } + ], + "valueType": "xs:string", + "value": "Released", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationShortName", + "displayName": [ + { + "language": "en", + "text": "Organization short name" + }, + { + "language": "de", + "text": "Kurzname der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI002-003" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company" + } + ], + "valueType": "xs:string", + "value": "M&M", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationOfficialName", + "displayName": [ + { + "language": "en", + "text": "Organization official name" + }, + { + "language": "de", + "text": "Offizieller Name der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI004#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI004-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company Ltd." + } + ], + "valueType": "xs:string", + "value": "M&M China", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "Title", + "displayName": [ + { + "language": "en", + "text": "Document title" + }, + { + "language": "de", + "text": "Dokumententitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABG940#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABG940-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary title@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Subtitle", + "displayName": [ + { + "language": "en", + "text": "Subtitle" + }, + { + "language": "de", + "text": "Untertitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH998#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH998-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary subtitle@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Description", + "displayName": [ + { + "language": "en", + "text": "Document description" + }, + { + "language": "de", + "text": "Dokumentenbeschreibung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN466#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN466-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Abstract@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "KeyWords", + "displayName": [ + { + "language": "en", + "text": "Keywords" + }, + { + "language": "de", + "text": "Stichworte" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH999#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH999-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary keywords@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "PreviewFile", + "displayName": [ + { + "language": "en", + "text": "Preview file" + }, + { + "language": "de", + "text": "Vorschaudatei" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK127#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK127-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.jpg" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "PreviewFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.jpg", + "contentType": "image/jpeg", + "modelType": "File" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + } + ], + "modelType": "SubmodelElementCollection" + }, + { + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003/0173-1#01-AHF579#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI500#003~0/0173-1#01-AHF579#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI500-003/0173-1-01-AHF579-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "DocumentIds", + "displayName": [ + { + "language": "en", + "text": "Document identifyers" + }, + { + "language": "de", + "text": "Dokumentidentifikatoren" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document identificator" + }, + { + "language": "de", + "text": "Dokumentidentifikator" + } + ], + "description": [ + { + "language": "en", + "text": "This SubmodelElementCollection holds the information for a VDI 2770 Document entity" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003/0173-1#01-AHF580#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI501#003~0/0173-1#01-AHF580#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI501-003/0173-1-01-AHF580-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "DocumentDomainId", + "displayName": [ + { + "language": "en", + "text": "document domain identificator" + }, + { + "language": "de", + "text": "Document Domain Identifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH994#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH994-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "https://domain.com/..." + } + ], + "valueType": "xs:string", + "value": "a3f1c2b4-9d81-4e5a-b6f2-01ac9e11d002", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIdentifier", + "displayName": [ + { + "language": "en", + "text": "Document Identifyer" + }, + { + "language": "de", + "text": "Dokumentennummer" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAO099#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAO099-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "XF90-884" + } + ], + "valueType": "xs:string", + "value": "DOC2025A002", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "DocumentIsPrimary", + "displayName": [ + { + "language": "en", + "text": "Document is primary" + }, + { + "language": "de", + "text": "Dokument ist primär" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH995#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH995-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "true" + } + ], + "valueType": "xs:boolean", + "value": "True", + "modelType": "Property" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentClassifications", + "displayName": [ + { + "language": "en", + "text": "Document classifications" + }, + { + "language": "de", + "text": "Dokumentklassifikationen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document classification" + }, + { + "language": "de", + "text": "Dokumentklassifikation" + } + ], + "description": [ + { + "language": "en", + "text": "Set of information for describing the classification of the Document according to a ClassificationSystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003/0173-1#01-AHF581#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI502#003~0/0173-1#01-AHF581#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI502-003/0173-1-01-AHF581-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "ClassId", + "displayName": [ + { + "language": "en", + "text": "Class identificator" + }, + { + "language": "de", + "text": "Klassenidentifikator" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH996#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH996-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "03-02" + } + ], + "valueType": "xs:string", + "value": "CLS-004", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassificationSystem", + "displayName": [ + { + "language": "en", + "text": "Classification system" + }, + { + "language": "de", + "text": "Klassifizierungssystem" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH997#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH997-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "VDI2770:2020" + } + ], + "valueType": "xs:string", + "value": "UNSPSC", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassName", + "displayName": [ + { + "language": "en", + "text": "Class Name" + }, + { + "language": "de", + "text": "Klassenname" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABJ219#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABJ219-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Operation@en" + } + ], + "modelType": "MultiLanguageProperty" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DocumentVersions", + "displayName": [ + { + "language": "en", + "text": "Document versions" + }, + { + "language": "de", + "text": "Dokumentenversionen" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "semanticIdListElement": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Document version" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003/0173-1#01-AHF582#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI503#003~0/0173-1#01-AHF582#003" + }, + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI503-003/0173-1-01-AHF582-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "idShort": "Language", + "displayName": [ + { + "language": "en", + "text": "Language" + }, + { + "language": "de", + "text": "Sprache" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN468-008" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "Property", + "valueTypeListElement": "xs:string", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "en (English)" + }, + { + "language": "de", + "text": "en (Englisch)" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN468#008" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "en" + } + ], + "valueType": "xs:string", + "value": "de", + "valueId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#07-AAS045#003" + } + ] + }, + "modelType": "Property" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "DigitalFiles", + "displayName": [ + { + "language": "en", + "text": "Digital files" + }, + { + "language": "de", + "text": "Digitale Dateien" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK126-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "orderRelevant": false, + "typeValueListElement": "File", + "value": [ + { + "displayName": [ + { + "language": "en", + "text": "Name of the specific digital file@en" + }, + { + "language": "de", + "text": "Name der spezifischen digitalen Datei@de" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK126#002" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.PDF" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "DigitalFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.pdf", + "contentType": "application/pdf", + "modelType": "File" + } + ], + "modelType": "SubmodelElementList" + }, + { + "category": "PARAMETER", + "idShort": "Version", + "displayName": [ + { + "language": "en", + "text": "Document version" + }, + { + "language": "de", + "text": "Dokumentenversion" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAP003#005" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAP003-005" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "V1.2" + } + ], + "valueType": "xs:string", + "value": "2.3", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusSetDate", + "displayName": [ + { + "language": "en", + "text": "Document status set date" + }, + { + "language": "de", + "text": "Datum der Einstellung des Dokumentenstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI000#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI000-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "2020-02-06" + } + ], + "valueType": "xs:date", + "value": "2025-10-10", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "StatusValue", + "displayName": [ + { + "language": "en", + "text": "Document status" + }, + { + "language": "de", + "text": "Dokumentstatus" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI001#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI001-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Released" + } + ], + "valueType": "xs:string", + "value": "InReview", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationShortName", + "displayName": [ + { + "language": "en", + "text": "Organization short name" + }, + { + "language": "de", + "text": "Kurzname der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI002-003" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company" + } + ], + "valueType": "xs:string", + "value": "M&M", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "OrganizationOfficialName", + "displayName": [ + { + "language": "en", + "text": "Organization official name" + }, + { + "language": "de", + "text": "Offizieller Name der Organisation" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI004#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABI004-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Example company Ltd." + } + ], + "valueType": "xs:string", + "value": "M&M Germany", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "Title", + "displayName": [ + { + "language": "en", + "text": "Document title" + }, + { + "language": "de", + "text": "Dokumententitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABG940#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABG940-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary title@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Subtitle", + "displayName": [ + { + "language": "en", + "text": "Subtitle" + }, + { + "language": "de", + "text": "Untertitel" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH998#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH998-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary subtitle@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "Description", + "displayName": [ + { + "language": "en", + "text": "Document description" + }, + { + "language": "de", + "text": "Dokumentenbeschreibung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-AAN466#004" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-AAN466-004" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Abstract@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "KeyWords", + "displayName": [ + { + "language": "en", + "text": "Keywords" + }, + { + "language": "de", + "text": "Stichworte" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABH999#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABH999-003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Examplary keywords@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "PreviewFile", + "displayName": [ + { + "language": "en", + "text": "Preview file" + }, + { + "language": "de", + "text": "Vorschaudatei" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK127#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK127-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "docu_cecc_fullmanual_DE.jpg" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/AllowedIdShort/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "AllowedIdShort", + "valueType": "xs:string", + "value": "PreviewFile[\\d{2,3}]" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/dummy_document.jpg", + "contentType": "image/jpeg", + "modelType": "File" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "Entities", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/vdi/2770/1/0/EntitiesForDocumentation" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "orderRelevant": false, + "typeValueListElement": "Entity", + "modelType": "SubmodelElementList" + } + ] +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_WithCursor_Expected.json b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_WithCursor_Expected.json new file mode 100644 index 00000000..0e50c0c3 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetAllSubmodelElementsByAasId_WithCursor_Expected.json @@ -0,0 +1,39 @@ +{ + "paging_metadata": { + "cursor": null + }, + "result": [ + { + "idShort": "Entities", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/vdi/2770/1/0/EntitiesForDocumentation" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "orderRelevant": false, + "typeValueListElement": "Entity", + "modelType": "SubmodelElementList" + } + ] +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetSubmodelByAasId_Expected.json b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetSubmodelByAasId_Expected.json new file mode 100644 index 00000000..4cfeff87 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin.PlaywrightTests/AasRepository/TestData/GetSubmodelByAasId_Expected.json @@ -0,0 +1,1603 @@ +{ + "idShort": "CustomSubmodel", + "description": [ + { + "language": "en", + "text": "The Submodel HierarchicalStructures identified by its semanticId. The Submodel idShort can be picked freely." + } + ], + "id": "https://mm-software.com/submodel/000-001/CustomSubmodel", + "kind": "Instance", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/CustomSubmodel/Submodel/Template/0/1" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "submodelElements": [ + { + "idShort": "EntryNode", + "description": [ + { + "language": "en", + "text": "Base entry point for the Entity tree in this Submodel, this must be a Self-managed Entity reflecting the Assets administrated in the AAS this Submodel is part of." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/EntryNode/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "statements": [ + { + "idShort": "Node", + "description": [ + { + "language": "en", + "text": "Base entry point for the Entity tree in this Submodel, this must be a Self-managed Entity reflecting the Assets administrated in the Asset Administration Shell this Submodel is part of. The idShort of the EntryNode can be picked freely and may reflect a name of the asset." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/Node/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "statements": [ + { + "idShort": "Node", + "description": [ + { + "language": "en", + "text": "Can be a Co-managed or Self-managed entity. A Node reflects an element in the hierarchical model is set into relation with one or more defined relations. The name of a node can be picked freely but it must be unique in its hierarchical (sub-)level." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/ChildNode/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "entityType": "SelfManagedEntity", + "globalAssetId": "https://mm-software.com/ids/assets/000-003", + "modelType": "Entity" + }, + { + "idShort": "SameAs", + "description": [ + { + "language": "en", + "text": "Reference between two Entities in the same Submodel or across Submodels." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/SameAs/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ModelReference", + "keys": [] + }, + "second": { + "type": "ModelReference", + "keys": [] + }, + "modelType": "RelationshipElement" + }, + { + "idShort": "IsPartOf", + "description": [ + { + "language": "en", + "text": "Modeling of logical connections between components and sub-components. Either this or \"HasPart\" must be used, not both." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/IsPartOf/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ModelReference", + "keys": [] + }, + "second": { + "type": "ModelReference", + "keys": [] + }, + "modelType": "RelationshipElement" + }, + { + "idShort": "HasPart", + "description": [ + { + "language": "en", + "text": "Modeling of logical connections between components and sub-components. Either this or \"IsPartOf\" must be used, not both." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/HasPart/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "EditIdShort", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ModelReference", + "keys": [] + }, + "second": { + "type": "ModelReference", + "keys": [] + }, + "modelType": "RelationshipElement" + }, + { + "idShort": "BulkCount", + "description": [ + { + "language": "en", + "text": "To be used if bulk components are referenced, e.g., a 10x M4x30 screw." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/BulkCount/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "valueType": "xs:unsignedLong", + "value": "52467", + "modelType": "Property" + } + ], + "entityType": "SelfManagedEntity", + "globalAssetId": "https://mm-software.com/ids/assets/000-002", + "modelType": "Entity" + }, + { + "idShort": "SameAs", + "description": [ + { + "language": "en", + "text": "Reference between two Entities in the same Submodel or across Submodels." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/SameAs/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://company.com" + } + ] + }, + "second": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://www.mm-software.com/" + } + ] + }, + "modelType": "RelationshipElement" + }, + { + "idShort": "IsPartOf", + "description": [ + { + "language": "en", + "text": "Modeling of logical connections between asset and sub-asset. Either this or \"HasPart\" must be used, not both." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/IsPartOf/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://www.mm-software.com/" + } + ] + }, + "second": { + "type": "ModelReference", + "keys": [ + { + "type": "Submodel", + "value": "https://mm-software.com/submodel/000-003/CustomSubmodel" + }, + { + "type": "Property", + "value": "ArcheType" + } + ] + }, + "modelType": "RelationshipElement" + }, + { + "idShort": "HasPart", + "description": [ + { + "language": "en", + "text": "Modeling of logical connections between components and sub-components. Either this or \"IsPartOf\" must be used, not both." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/HasPart/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + }, + { + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "True" + } + ], + "first": { + "type": "ModelReference", + "keys": [ + { + "type": "Submodel", + "value": "https://mm-software.com/submodel/000-003/CustomSubmodel" + }, + { + "type": "SubmodelElementList", + "value": "Markings" + }, + { + "type": "SubmodelElementCollection", + "value": "0" + }, + { + "type": "Property", + "value": "DesignationOfCertificateOrApproval" + } + ] + }, + "second": { + "type": "ModelReference", + "keys": [ + { + "type": "Submodel", + "value": "https://mm-software.com/submodel/000-003/ContactInformation" + }, + { + "type": "SubmodelElementCollection", + "value": "ContactInformation0" + }, + { + "type": "Property", + "value": "NameOfContact" + } + ] + }, + "modelType": "RelationshipElement" + } + ], + "entityType": "SelfManagedEntity", + "globalAssetId": "https://mm-software.com/ids/assets/000-003", + "modelType": "Entity" + }, + { + "idShort": "Target_model", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "http://admin-shell.io/aasx-package-explorer/functions/asciidoc/target-model/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "value": { + "type": "ModelReference", + "keys": [ + { + "type": "Submodel", + "value": "https://mm-software.com/submodel/000-002/ContactInformation" + } + ] + }, + "modelType": "ReferenceElement" + }, + { + "idShort": "Header", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "http://admin-shell.io/aasx-package-explorer/functions/asciidoc/heading1/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "value": "SW5mb3JtYXRpb24gc2V0IGZvciBTdWJtb2RlbCDigJxOYW1lcGxhdGXigJ0=", + "contentType": "text/markdown", + "modelType": "Blob" + }, + { + "category": "PARAMETER", + "idShort": "ArbitraryRange", + "description": [ + { + "language": "en", + "text": "Note: Every range property can be used." + }, + { + "language": "en", + "text": "Note: The idShort is arbitrary." + }, + { + "language": "en", + "text": "Note: The use of a displayName is recommended." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SMT/General/Arbitrary" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/SMT/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:double", + "min": "24", + "max": "962", + "modelType": "Range" + }, + { + "category": "CONSTANT", + "idShort": "ArcheType", + "description": [ + { + "language": "en", + "text": "ArcheType of the Submodel, there are three allowed enumeration entries: 1. “Full”, 2. “OneDown” and 3. “OneUp”. " + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/idta/HierarchicalStructures/ArcheType/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "kind": "TemplateQualifier", + "type": "FormChoices", + "valueType": "xs:string", + "value": "Full;OneDown;OneUp" + } + ], + "valueType": "xs:string", + "value": "000-001 ArcheType", + "modelType": "Property" + }, + { + "idShort": "Markings", + "description": [ + { + "language": "en", + "text": "Note: CE marking is declared as mandatory according to EU Blue Guide" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61360_7#AAS006#001" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI563#003/0173-1#01-AHF849#003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "orderRelevant": false, + "typeValueListElement": "SubmodelElementCollection", + "value": [ + { + "idShort": "Marking", + "description": [ + { + "language": "en", + "text": "Note: CE marking is declared as mandatory according to the Blue Guide of the EU-Commission" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61360_7#AAS009#001#0" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI564#003/0173-1#01-AHF850#003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "value": [ + { + "category": "PARAMETER", + "idShort": "ReferenceElementWithOutValue", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/vdi/2770/1/0/Document/DocumentedEntity" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "OneToMany" + } + ], + "modelType": "ReferenceElement" + }, + { + "idShort": "MarkingName", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABA231#009" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI190#003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "valueType": "xs:string", + "value": "0173-1#07-DAA603#004", + "modelType": "Property" + }, + { + "idShort": "DesignationOfCertificateOrApproval", + "description": [ + { + "language": "en", + "text": "Note: Approval identifier, reference to the certificate number, to be entered without spaces " + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABH783#003" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI975#002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "valueType": "xs:string", + "value": "KEMA99IECEX1105/128", + "modelType": "Property" + }, + { + "idShort": "IssueDate", + "description": [ + { + "language": "en", + "text": "Note: format by lexical representation: CCYY-MM-DD Note: to be specified to the day " + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABO097#001" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABL774#001" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "valueType": "xs:date", + "value": "2022-01-01", + "modelType": "Property" + }, + { + "idShort": "ExpiryDate", + "description": [ + { + "language": "en", + "text": "Note: format by lexical representation: CCYY-MM-DD Note: to be specified to the day " + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABH830#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABL775#001" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "valueType": "xs:date", + "value": "2022-01-01", + "modelType": "Property" + }, + { + "idShort": "MarkingAdditionalText", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABB146#007" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI192#003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:string", + "value": "0044", + "modelType": "Property" + }, + { + "category": "PARAMETER", + "idShort": "ClassName", + "displayName": [ + { + "language": "en", + "text": "Class Name" + }, + { + "language": "en", + "text": "Klassenname" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABJ219#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABJ219-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + }, + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/ExampleValue/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "ExampleValue", + "valueType": "xs:string", + "value": "Operation@en" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "category": "PARAMETER", + "idShort": "ArbitraryRangeString", + "description": [ + { + "language": "en", + "text": "Note: Every range property can be used." + }, + { + "language": "en", + "text": "Note: The idShort is arbitrary." + }, + { + "language": "en", + "text": "Note: The use of a displayName is recommended." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SMT/General/Arbitrary/string" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/SMT/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:string", + "min": "2", + "max": "10", + "modelType": "Range" + }, + { + "idShort": "MarkingFile", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABO100#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABI191#003" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "TemplateQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/data/checkmark.png", + "contentType": "image/png", + "modelType": "File" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "SubmodelElementList" + }, + { + "idShort": "OperatingConditionsOfReliabilityCharacteristics", + "description": [ + { + "language": "en", + "text": "operating conditions of reliability characteristics" + }, + { + "language": "fr", + "text": "conditions de fonctionnement des caractéristiques de fiabilité et de sécurité fonctionnelle" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///62683#ACG071#001" + } + ] + }, + "qualifiers": [ + { + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "value": [ + { + "idShort": "CompanyLogo", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/zvei/nameplate/2/0/Nameplate/CompanyLogo" + } + ] + }, + "qualifiers": [ + { + "kind": "ConceptQualifier", + "type": "Multiplicity", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "value": "https://raw.githubusercontent.com/AAS-TwinEngine/AAS.TwinEngine.DataEngine/refs/heads/main/example/logo/MM_Logo.svg", + "contentType": "EMPTY", + "modelType": "File" + }, + { + "category": "PARAMETER", + "idShort": "ArbitraryRangeDate", + "description": [ + { + "language": "en", + "text": "Note: Every range property can be used." + }, + { + "language": "en", + "text": "Note: The idShort is arbitrary." + }, + { + "language": "en", + "text": "Note: The use of a displayName is recommended." + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SMT/General/Arbitrary/date" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/SMT/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:date", + "min": "2010-02-02", + "max": "2030-02-06", + "modelType": "Range" + }, + { + "idShort": "RatedVoltage", + "description": [ + { + "language": "en", + "text": "rated voltage" + }, + { + "language": "fr", + "text": "tension assignée" + }, + { + "language": "de", + "text": "Bemessungsspannung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABA588#004" + } + ] + }, + "qualifiers": [ + { + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:double", + "value": "56.5", + "modelType": "Property" + }, + { + "idShort": "MinimumRatedVoltage", + "description": [ + { + "language": "en", + "text": "minimum rated voltage" + }, + { + "language": "fr", + "text": "tension assignée minimale" + }, + { + "language": "de", + "text": "minimale Bemessungsspannung" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0112/2///61987#ABD461#004" + } + ] + }, + "qualifiers": [ + { + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToMany" + } + ], + "valueType": "xs:double", + "value": "12.5", + "modelType": "Property" + }, + { + "idShort": "Paragraph", + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "http://admin-shell.io/aasx-package-explorer/functions/asciidoc/textblock/1/0" + } + ] + }, + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "One" + } + ], + "value": "UmVnYXJkaW5nIHByb3BlcnR5IGBNYXJraW5nTmFtZWAsIHRoZSBwcmVmZXJhYmxlIHNvbHV0aW9uIGlzIHRvIHByb3ZpZGUgYSB2YWx1ZUlkIGluIElSREkgb3JpZ2luYXRpbmcgZnJvbSBJRUMgQ0REIG9yIEVDTEFTUyBlbnVtZXJhdGlvbiB2YWx1ZSBsaXN0LCBlLmcuICJDRSIgKElSREk6IDAxMTIvMi8vLzYxOTg3I0FCTzQwOSMwMDMgb3IgMDE3My0xIzA3LURBQTYwMyMwMDQpLiBJbiBjYXNlIG5vbmUgb2YgdGhlIGV4aXN0aW5nIEVDTEFTUyBlbnVtZXJhdGlvbiB2YWx1ZXMgbWF0Y2hlcywgZmlsbGluZyBwbGFpbiBzdHJpbmcgdGV4dCBpbnRvIHRoZSDigJx2YWx1ZeKAnSBmaWVsZCBvZiB0aGUgcHJvcGVydHkgYE1hcmtpbmdOYW1lYCBjYW4gYmUgYWNjZXB0ZWQgYWx0ZXJuYXRpdmVseS4gSXQgbmVlZHMgdG8gYmUgcG9pbnRlZCBvdXQgdGhhdCBFQ0xBU1MgYWxzbyBwcm92aWRlcyBtYXJraW5nIGRlZmluaXRpb25zIGluIHRlcm1zIG9mIGJvb2xlYW4gcHJvcGVydHksIGUuZy4g4oCcQ0UtIHF1YWxpZmljYXRpb24gcHJlc2VudOKAnSAoSVJESTogMDE3My0xIzAyLUJBRjA1MyMwMDgpLiBJbiB0aGlzIGNhc2UgdXNlcnMgc2hvdWxkIGluc3RlYWQgdXNlIGEgbWF0Y2hpbmcgRUNMQVNTIGVudW1lcmF0aW9uIHZhbHVlIG9yLCBpZiBub3QgcHJvdmlkZWQgYXMgZW51bWVyYXRpb24sIGZpbGwgaW4gcGxhaW4gc3RyaW5nIHRleHQuDQoNClRoZSBmb2xsb3dpbmcgZXhhbXBsZSAoc2VlIDw8RmlndXJlXzU+PikgaWxsdXN0cmF0ZXMgaG93IHRvIG1vZGVsIHByb2R1Y3QgbWFya2luZyBpbiBhbiBBQVMuIE9uIHRoZSBsZWZ0IHNpZGUgdGhlcmUgaXMgYSBzYW1wbGUgbmFtZXBsYXRlIHdoaWNoIGNvbnRhaW5zIHR3byBtYXJraW5ncyB0byBiZSBtb2RlbGxlZDogdGhlIENFIG1hcmtpbmcgYW5kIHRoZSBXRUVFIG1hcmtpbmcgd2l0aCBhIGNyb3NzZWQtb3V0IHdoZWVsZWQgYmluLiBOZXh0IHRvIHRoZSBuYW1lcGxhdGUgYSB0YWJsZSBsaXN0cyBhbGwgcHJvcGVydGllcyBhbmQgdGhlaXIgYXR0cmlidXRlcy4gDQo=", + "contentType": "text/markdown", + "modelType": "Blob" + }, + { + "idShort": "ProductClassName", + "displayName": [ + { + "language": "en", + "text": "Product class name" + }, + { + "language": "de", + "text": "Produktklasse Name" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABK273#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABK273-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/SMT/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "value": [ + { + "language": "en", + "text": "Name of product class in the used classification system" + }, + { + "language": "de", + "text": "Name der Produktklasse im verwendeten Klassifizierungssystem" + }, + { + "language": "", + "text": "" + } + ], + "modelType": "MultiLanguageProperty" + }, + { + "idShort": "ModelReference", + "displayName": [ + { + "language": "en", + "text": "Reference to technical property area" + }, + { + "language": "de", + "text": "Referenz auf einen technsichen Merkmalsbereich" + } + ], + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "0173-1#02-ABL358#002" + } + ] + }, + "supplementalSemanticIds": [ + { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://api.eclass-cdp.com/0173-1-02-ABL358-002" + } + ] + } + ], + "qualifiers": [ + { + "semanticId": { + "type": "ExternalReference", + "keys": [ + { + "type": "GlobalReference", + "value": "https://admin-shell.io/SubmodelTemplates/SMT/Cardinality/1/0" + } + ] + }, + "kind": "ConceptQualifier", + "type": "SMT/Cardinality", + "valueType": "xs:string", + "value": "ZeroToOne" + } + ], + "value": { + "type": "ModelReference", + "keys": [ + { + "type": "Submodel", + "value": "https://mm-software.com/submodel/000-003/HandoverDocumentation" + }, + { + "type": "SubmodelElementList", + "value": "Documents" + }, + { + "type": "SubmodelElementCollection", + "value": "0" + } + ] + }, + "modelType": "ReferenceElement" + } + ], + "modelType": "SubmodelElementCollection" + } + ], + "modelType": "Submodel" +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru new file mode 100644 index 00000000..689f9d7e --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptor by submodelId and aasId.bru @@ -0,0 +1,21 @@ +meta { + name: Get Submodel Descriptor by submodelId and aasId + type: http + seq: 4 +} + +get { + url: {{DataEngineBaseUrl}}/shell-descriptors/:aasIdentifier/submodel-descriptors/:submodelIdentifier + body: none + auth: inherit +} + +params:path { + submodelIdentifier: {{submodelIdentifierHandoverDocumentation}} + aasIdentifier: {{aasIdentifier}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru new file mode 100644 index 00000000..15b96ee9 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Registry/Get Submodel Descriptors by aasId.bru @@ -0,0 +1,20 @@ +meta { + name: Get Submodel Descriptors by aasId + type: http + seq: 3 +} + +get { + url: {{DataEngineBaseUrl}}/shell-descriptors/:aasIdentifier/submodel-descriptors + body: none + auth: inherit +} + +params:path { + aasIdentifier: {{aasIdentifier}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel by submodelId and aaslId.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel by submodelId and aaslId.bru new file mode 100644 index 00000000..f1da7644 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel by submodelId and aaslId.bru @@ -0,0 +1,26 @@ +meta { + name: Get Submodel by submodelId and aaslId + type: http + seq: 8 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + submodelIdentifier: {{submodelIdentifierCustomSubmodel}} + aasIdentifier: {{aasIdentifier}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-element by idshort and aasID.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-element by idshort and aasID.bru new file mode 100644 index 00000000..3b959f9c --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-element by idshort and aasID.bru @@ -0,0 +1,27 @@ +meta { + name: Get Submodel-element by idshort and aasID + type: http + seq: 10 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath + body: none + auth: inherit +} + +params:query { + ~level: + ~extent: +} + +params:path { + idShortPath: Documents[0].DocumentClassifications[0].ClassName + aasIdentifier: {{aasIdentifier}} + submodelIdentifier: {{submodelIdentifierHandoverDocumentation}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-elements by aasID.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-elements by aasID.bru new file mode 100644 index 00000000..7310ba8c --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get Submodel-elements by aasID.bru @@ -0,0 +1,26 @@ +meta { + name: Get Submodel-elements by aasID + type: http + seq: 9 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements + body: none + auth: inherit +} + +params:query { + ~limit: + ~cursor: +} + +params:path { + aasIdentifier: {{aasIdentifier}} + submodelIdentifier: {{submodelIdentifierHandoverDocumentation}} +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get file attachment by idshort and aasID.bru b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get file attachment by idshort and aasID.bru new file mode 100644 index 00000000..fa32e2b7 --- /dev/null +++ b/source/AAS.TwinEngine.Plugin.TestPlugin/Example/apiCollection/Aas Repository/Get file attachment by idshort and aasID.bru @@ -0,0 +1,22 @@ +meta { + name: Get file attachment by idshort and aasID + type: http + seq: 11 +} + +get { + url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/submodels/:submodelIdentifier/submodel-elements/:idShortPath/attachment + body: none + auth: inherit +} + +params:path { + idShortPath: Documents%255B0%255D.DocumentVersions%255B0%255D.DigitalFiles%255B0%255D + aasIdentifier: {{aasIdentifier}} + submodelIdentifier: {{submodelIdentifierHandoverDocumentation}} +} + +settings { + encodeUrl: true + timeout: 0 +}