Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
eb6ba2e
refactor: Change public methods to private in SemanticIdHandler for e…
mm-kgi Mar 5, 2026
21cd268
added implementation for refactoring semanticidhandler
mm-kgi Mar 6, 2026
332a6dd
Added Refactored SemanticIdHandler
mm-kgi Mar 12, 2026
23f6f39
git merge with parent branch
mm-kgi Mar 18, 2026
f92778a
git merge from parent barnch
mm-kgi Mar 18, 2026
4ad1241
Refactor the code to remove warnings
mm-kgi Mar 18, 2026
040b9ca
refactor to remove warnings
mm-kgi Mar 18, 2026
c75f9d6
remove internal methods test cases
mm-kgi Mar 18, 2026
91a1d0e
change dataengine image
mm-kgi Mar 18, 2026
1501c54
remove cognitive complexity inside submodel filler
mm-kgi Mar 18, 2026
30fc78e
remove unused using
mm-kgi Mar 18, 2026
b7ee7c5
Added internal semantic id removal logic from submodel qualifiers
mm-kgi Mar 20, 2026
c8df6fe
remove unused var
mm-kgi Mar 20, 2026
9c3cff8
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 20, 2026
dd01f18
remove internal semantic from test data
mm-kgi Mar 20, 2026
f2b9362
Revert "remove internal semantic from test data"
mm-kgi Mar 20, 2026
60432c8
remove internal semantic id from qualifires in testdata
mm-kgi Mar 20, 2026
54fe06d
remove internal semantic ids from testdata of playwrite tests
mm-kgi Mar 20, 2026
d414972
remove extra property added in testdata json
mm-kgi Mar 20, 2026
7d24721
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 20, 2026
a57bd66
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 24, 2026
6461331
Added v2 config file with backward compatibility
mm-kgi Apr 7, 2026
a490ede
remove unused usings
mm-kgi Apr 7, 2026
497f6f2
git merge parent branch
mm-kgi Apr 7, 2026
174a7b6
git merge parent branch
mm-kgi Apr 7, 2026
f9f830a
Added sonarqube suggestions
mm-kgi Apr 7, 2026
850d06d
remove extra new config file added for test
mm-kgi Apr 7, 2026
d77272c
refactor docker compose and logging extenstion to add root level
mm-kgi Apr 7, 2026
3b4703d
refactor Obsolete tags
mm-kgi Apr 8, 2026
b0f0043
Added suppourt for multiple repositories wit backward compebility
mm-kgi Apr 9, 2026
fad381d
Refactor compose to add template repo
mm-kgi Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 40 additions & 37 deletions example/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,46 @@ services:
condition: service_started
restart: always
environment:
- AasEnvironment__DataEngineRepositoryBaseUrl=http://localhost:8080
- AasEnvironment__AasEnvironmentRepositoryBaseUrl=http://template-repository:8081
- AasEnvironment__AasRegistryBaseUrl=http://aas-template-registry:8080
- AasEnvironment__SubModelRegistryBaseUrl=http://sm-template-registry:8080
- AasEnvironment__CustomerDomainUrl=https://mm-software.com
- TemplateMappingRules__SubmodelTemplateMappings__0__templateId=https://admin-shell.io/ZVEI/TechnicalData/Submodel/1/2
- TemplateMappingRules__SubmodelTemplateMappings__0__pattern__0=TechnicalData
- TemplateMappingRules__SubmodelTemplateMappings__1__templateId=https://admin-shell.io/idta/SubmodelTemplate/DigitalNameplate/3/0
- TemplateMappingRules__SubmodelTemplateMappings__1__pattern__0=Nameplate
- TemplateMappingRules__SubmodelTemplateMappings__2__templateId=https://admin-shell.io/idta/SubmodelTemplate/ContactInformation/1/0
- TemplateMappingRules__SubmodelTemplateMappings__2__pattern__0=ContactInformation
- TemplateMappingRules__SubmodelTemplateMappings__3__templateId=https://admin-shell.io/idta/SubmodelTemplate/CarbonFootprint/1/0
- TemplateMappingRules__SubmodelTemplateMappings__3__pattern__0=CarbonFootprint
- TemplateMappingRules__SubmodelTemplateMappings__4__templateId=https://admin-shell.io/idta/SubmodelTemplate/HandoverDocumentation/2/0
- TemplateMappingRules__SubmodelTemplateMappings__4__pattern__0=HandoverDocumentation
- TemplateMappingRules__ShellTemplateMappings__0__templateId=https://mm-software.com/aas/aasTemplate
- TemplateMappingRules__ShellTemplateMappings__0__pattern__0=
- TemplateMappingRules__AasIdExtractionRules__0__pattern=Regex
- TemplateMappingRules__AasIdExtractionRules__0__index=6
- TemplateMappingRules__AasIdExtractionRules__0__separator=/
- PluginConfig__Plugins__0__PluginName=Plugin1
- PluginConfig__Plugins__0__PluginUrl=http://dpp-plugin:8080
- HeaderForwarding__HeaderMappings__TemplateRepository__0__Source=Authorization
- HeaderForwarding__HeaderMappings__TemplateRepository__0__Target=Authorization
- HeaderForwarding__HeaderMappings__TemplateRepository__0__Required=false
- HeaderForwarding__HeaderMappings__TemplateRepository__1__Source=X-User-Roles
- HeaderForwarding__HeaderMappings__TemplateRepository__1__Target=X-Template-Access-Roles
- HeaderForwarding__HeaderMappings__TemplateRepository__1__Required=false
- HeaderForwarding__HeaderMappings__TemplateRegistry__0__Source=Authorization
- HeaderForwarding__HeaderMappings__TemplateRegistry__0__Target=Authorization
- HeaderForwarding__HeaderMappings__TemplateRegistry__0__Required=false
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__0__Source=Authorization
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__0__Target=X-Auth-Token
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__0__Required=false
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__1__Source=X-Organization-Id
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__1__Target=X-Tenant-Context
- HeaderForwarding__HeaderMappings__Plugins__Plugin1__1__Required=false
- General__DataEngineRepositoryBaseUrl=http://localhost:8080
- General__CustomerDomainUrl=https://mm-software.com
- Plugins__Instances__0__Name=RelationalDatabasePlugin
- Plugins__Instances__0__baseUrl=http://dpp-plugin:8080
- Plugins__Instances__0__headerMappings__0__source=Authorization
- Plugins__Instances__0__headerMappings__0__target=X-Auth-Token
- Plugins__Instances__0__headerMappings__0__required=false
- Plugins__Instances__0__headerMappings__1__source=X-Organization-Id
- Plugins__Instances__0__headerMappings__1__target=X-Tenant-Context
- Plugins__Instances__0__headerMappings__1__required=false
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__0__templateId=https://admin-shell.io/ZVEI/TechnicalData/Submodel/1/2
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__0__pattern__0=TechnicalData
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__1__templateId=https://admin-shell.io/idta/SubmodelTemplate/DigitalNameplate/3/0
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__1__pattern__0=Nameplate
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__2__templateId=https://admin-shell.io/idta/SubmodelTemplate/ContactInformation/1/0
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__2__pattern__0=ContactInformation
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__3__templateId=https://admin-shell.io/idta/SubmodelTemplate/CarbonFootprint/1/0
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__3__pattern__0=CarbonFootprint
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__4__templateId=https://admin-shell.io/idta/SubmodelTemplate/HandoverDocumentation/2/0
- TemplateManagement__TemplateMappingRules__SubmodelTemplateMappings__4__pattern__0=HandoverDocumentation
- TemplateManagement__TemplateMappingRules__ShellTemplateMappings__0__templateId=https://mm-software.com/aas/aasTemplate
- TemplateManagement__TemplateMappingRules__ShellTemplateMappings__0__pattern__0=
- TemplateManagement__TemplateMappingRules__AasIdExtractionRules__0__Pattern=Regex
- TemplateManagement__TemplateMappingRules__AasIdExtractionRules__0__Index=6
- TemplateManagement__TemplateMappingRules__AasIdExtractionRules__0__Separator=/
- TemplateManagement__TemplateRepository__Name=AasTemplateRepository
- TemplateManagement__TemplateRepository__baseUrl=http://template-repository:8081
- TemplateManagement__TemplateRepository__headerMappings__0__source=Authorization
- TemplateManagement__TemplateRepository__headerMappings__0__target=Authorization
- TemplateManagement__TemplateRepository__headerMappings__0__required=false
- TemplateManagement__AasTemplateRegistry__Name=AasTemplateRegistry
- TemplateManagement__AasTemplateRegistry__baseUrl=http://aas-template-registry:8080
- TemplateManagement__AasTemplateRegistry__headerMappings__0__source=Authorization
- TemplateManagement__AasTemplateRegistry__headerMappings__0__target=Authorization
- TemplateManagement__AasTemplateRegistry__headerMappings__0__required=false
- TemplateManagement__SubmodelTemplateRegistry__Name=SubmodelTemplateRegistry
- TemplateManagement__SubmodelTemplateRegistry__baseUrl=http://sm-template-registry:8080
- TemplateManagement__SubmodelTemplateRegistry__headerMappings__0__source=Authorization
- TemplateManagement__SubmodelTemplateRegistry__headerMappings__0__target=Authorization
- TemplateManagement__SubmodelTemplateRegistry__headerMappings__0__required=false
networks:
- twinengine-network

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<Content Include="TestData\v1-config\appsettings.json" CopyToOutputDirectory="PreserveNewest" />
<Content Include="TestData\v2-config\appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers;
using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients;
using AAS.TwinEngine.DataEngine.Infrastructure.Providers.PluginDataProvider.Config;
using AAS.TwinEngine.DataEngine.ModuleTests.Common;

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -19,34 +19,39 @@

namespace AAS.TwinEngine.DataEngine.ModuleTests.Api.Services.AasRegistry;

public class ShellDescriptorControllerTests : IClassFixture<WebApplicationFactory<Program>>
public abstract class ShellDescriptorControllerTestsBase : IDisposable
{
private readonly ConfigTestFactory _factory;
private readonly ITemplateProvider _mockTemplateProvider;
private readonly HttpClient _client;
private readonly ICreateClient _httpClientFactory;

public ShellDescriptorControllerTests(WebApplicationFactory<Program> factory)
protected ShellDescriptorControllerTestsBase(string configDir)
{
_mockTemplateProvider = Substitute.For<ITemplateProvider>();
var mockPluginManifestProvider = Substitute.For<IPluginManifestProvider>();
var mockPluginManifestConflictHandler = Substitute.For<IPluginManifestConflictHandler>();
_httpClientFactory = Substitute.For<ICreateClient>();

var factory1 = factory.WithWebHostBuilder(builder =>
_factory = new ConfigTestFactory(configDir, services =>
{
_ = builder.ConfigureServices(services =>
{
_ = services.AddSingleton(_httpClientFactory);
_ = services.AddSingleton(mockPluginManifestProvider);
_ = services.AddSingleton(_mockTemplateProvider);
_ = services.AddSingleton(mockPluginManifestConflictHandler);
});
_ = services.AddSingleton(_httpClientFactory);
_ = services.AddSingleton(mockPluginManifestProvider);
_ = services.AddSingleton(_mockTemplateProvider);
_ = services.AddSingleton(mockPluginManifestConflictHandler);
});

_client = factory1.CreateClient();
_client = _factory.CreateClient();
_ = mockPluginManifestConflictHandler.Manifests.Returns(TestData.CreatePluginManifests());
}

public void Dispose()
{
_client.Dispose();
_factory.Dispose();
GC.SuppressFinalize(this);
}

[Fact]
public async Task GetAllShellDescriptorsAsync_ReturnsOkAsync()
{
Expand All @@ -71,10 +76,10 @@ public async Task GetAllShellDescriptorsAsync_ReturnsOkAsync()
httpClientPlugin2.BaseAddress = new Uri("https://testendpoint2.com");

const string HttpClientNamePlugin1 = $"{PluginConfig.HttpClientNamePrefix}TestPlugin1";
_httpClientFactory.CreateClient(HttpClientNamePlugin1).Returns(httpClientPlugin1);
_ = _httpClientFactory.CreateClient(HttpClientNamePlugin1).Returns(httpClientPlugin1);

const string HttpClientNamePlugin2 = $"{PluginConfig.HttpClientNamePrefix}TestPlugin2";
_httpClientFactory.CreateClient(HttpClientNamePlugin2).Returns(httpClientPlugin2);
_ = _httpClientFactory.CreateClient(HttpClientNamePlugin2).Returns(httpClientPlugin2);

_ = _mockTemplateProvider.GetShellDescriptorsTemplateAsync(Arg.Any<CancellationToken>()).Returns(template);

Expand Down Expand Up @@ -155,10 +160,10 @@ public async Task GetShellDescriptorByIdAsync_ReturnsOkAsync()
httpClient2.BaseAddress = new Uri("https://testendpoint2.com");

const string HttpClientName1 = $"{PluginConfig.HttpClientNamePrefix}TestPlugin1";
_httpClientFactory.CreateClient(HttpClientName1).Returns(httpClient1);
_ = _httpClientFactory.CreateClient(HttpClientName1).Returns(httpClient1);

const string HttpClientName2 = $"{PluginConfig.HttpClientNamePrefix}TestPlugin2";
_httpClientFactory.CreateClient(HttpClientName2).Returns(httpClient2);
_ = _httpClientFactory.CreateClient(HttpClientName2).Returns(httpClient2);

_ = _mockTemplateProvider.GetShellDescriptorsTemplateAsync(Arg.Any<CancellationToken>()).Returns(template);

Expand Down Expand Up @@ -213,7 +218,7 @@ public async Task GetShellDescriptorByIdAsync_WhenIdentifierIsInValid_Returns400
[Theory]
[InlineData("not-valid-base64!!!")]
[InlineData("invalid!!base64")]
public async Task GetShellDescriptorById_InvalidBase64_Returns400BadRequest(string invalidBase64)
public async Task GetShellDescriptorById_InvalidBase64_Returns400BadRequestAsync(string invalidBase64)
{
var response = await _client.GetAsync($"/shell-descriptors/{invalidBase64}");

Expand All @@ -227,7 +232,7 @@ public async Task GetShellDescriptorById_InvalidBase64_Returns400BadRequest(stri
[InlineData("<script>alert('xss')</script>")]
[InlineData("<img onerror=alert('xss')>")]
[InlineData("<svg/onload=alert('xss')>")]
public async Task GetShellDescriptorById_XssInDecodedId_Returns400BadRequest(string maliciousContent)
public async Task GetShellDescriptorById_XssInDecodedId_Returns400BadRequestAsync(string maliciousContent)
{
var encoded = EncodeBase64Url(maliciousContent);

Expand All @@ -243,7 +248,7 @@ public async Task GetShellDescriptorById_XssInDecodedId_Returns400BadRequest(str
[InlineData("'; DROP TABLE shells--")]
[InlineData("1 UNION SELECT * FROM descriptors")]
[InlineData("admin'; DELETE FROM shells--")]
public async Task GetShellDescriptorById_SqlInjectionInDecodedId_Returns400BadRequest(string maliciousContent)
public async Task GetShellDescriptorById_SqlInjectionInDecodedId_Returns400BadRequestAsync(string maliciousContent)
{
var encoded = EncodeBase64Url(maliciousContent);

Expand All @@ -259,7 +264,7 @@ public async Task GetShellDescriptorById_SqlInjectionInDecodedId_Returns400BadRe
[InlineData("..\\..\\..\\windows\\system32")]
[InlineData("%2e%2e/config")]
[InlineData("..%2fconfig")]
public async Task GetShellDescriptorById_PathTraversalInDecodedId_Returns400BadRequest(string maliciousContent)
public async Task GetShellDescriptorById_PathTraversalInDecodedId_Returns400BadRequestAsync(string maliciousContent)
{
var encoded = EncodeBase64Url(maliciousContent);

Expand All @@ -275,7 +280,7 @@ public async Task GetShellDescriptorById_PathTraversalInDecodedId_Returns400BadR
[InlineData("data:text/html,<script>")]
[InlineData("vbscript:msgbox('xss')")]
[InlineData("file:///etc/passwd")]
public async Task GetShellDescriptorById_DangerousProtocolInDecodedId_Returns400BadRequest(string maliciousContent)
public async Task GetShellDescriptorById_DangerousProtocolInDecodedId_Returns400BadRequestAsync(string maliciousContent)
{
var encoded = EncodeBase64Url(maliciousContent);

Expand All @@ -288,7 +293,7 @@ public async Task GetShellDescriptorById_DangerousProtocolInDecodedId_Returns400

[Theory]
[InlineData("test\0value")]
public async Task GetShellDescriptorById_NullByteInDecodedId_Returns400BadRequest(string contentWithNullByte)
public async Task GetShellDescriptorById_NullByteInDecodedId_Returns400BadRequestAsync(string contentWithNullByte)
{
var encoded = EncodeBase64Url(contentWithNullByte);

Expand All @@ -300,7 +305,7 @@ public async Task GetShellDescriptorById_NullByteInDecodedId_Returns400BadReques
}

[Fact]
public async Task GetShellDescriptorById_IdentifierExceedsMaxLength_Returns400BadRequest()
public async Task GetShellDescriptorById_IdentifierExceedsMaxLength_Returns400BadRequestAsync()
{
var longIdentifier = "https://example.com/" + new string('a', 2050);
var encoded = EncodeBase64Url(longIdentifier);
Expand All @@ -315,13 +320,13 @@ public async Task GetShellDescriptorById_IdentifierExceedsMaxLength_Returns400Ba
[InlineData("https://admin-shell.io/idta/aas/ContactInformation/1/0")]
[InlineData("urn:uuid:123e4567-e89b-12d3-a456-426614174000")]
[InlineData("https://mm-software.com/submodel/test/Nameplate")]
public async Task GetShellDescriptorById_ValidAasIdentifiers_DoesNotReturn400(string validIdentifier)
public async Task GetShellDescriptorById_ValidAasIdentifiers_DoesNotReturn400Async(string validIdentifier)
{
var encoded = EncodeBase64Url(validIdentifier);
_ = _mockTemplateProvider.GetShellDescriptorsTemplateAsync(Arg.Any<CancellationToken>())
.Throws(new ResourceNotFoundException());

var response = await _client.GetAsync($"/shell-descriptors/{encoded}").ConfigureAwait(false);
var response = await _client.GetAsync($"/shell-descriptors/{encoded}");

Assert.NotEqual(HttpStatusCode.BadRequest, response.StatusCode);
}
Expand All @@ -340,6 +345,16 @@ private static string EncodeBase64Url(string plainText)
}
}

public class ShellDescriptorControllerTests_V1Config : ShellDescriptorControllerTestsBase
{
public ShellDescriptorControllerTests_V1Config() : base("v1-config") { }
}

public class ShellDescriptorControllerTests_V2Config : ShellDescriptorControllerTestsBase
{
public ShellDescriptorControllerTests_V2Config() : base("v2-config") { }
}

public class FakeHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> send) : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public static string CreateShellDescriptors() => """
{
"interface": "AAS-3.0",
"protocolInformation": {
"href": "https://localhost:5059/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg",
"href": "http://localhost/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg",
"endpointProtocol": "http",
"endpointProtocolVersion": null,
"subprotocol": null,
Expand All @@ -165,7 +165,7 @@ public static string CreateShellDescriptors() => """
{
"interface": "AAS-3.0",
"protocolInformation": {
"href": "https://localhost:5059/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzIyMDYtMTYzMS8xMDAwLTg1OQ",
"href": "http://localhost/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzIyMDYtMTYzMS8xMDAwLTg1OQ",
"endpointProtocol": "http",
"endpointProtocolVersion": null,
"subprotocol": null,
Expand Down Expand Up @@ -197,7 +197,7 @@ public static string CreateShellDescriptor() => """
{
"interface": "AAS-3.0",
"protocolInformation": {
"href": "https://localhost:5059/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg",
"href": "http://localhost/shells/aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg",
"endpointProtocol": "http",
"endpointProtocolVersion": null,
"subprotocol": null,
Expand Down
Loading
Loading