Skip to content

Commit

Permalink
feat(policy): add value check to post endpoint (#97)
Browse files Browse the repository at this point in the history
* technicalkey validation with error message added
* value validation with error message added

---------

Refs: #68
Co-authored-by: Phil Schneider <[email protected]>
Reviewed-by: Phil Schneider <[email protected]>
  • Loading branch information
AnuragNagpure and Phil91 authored Apr 17, 2024
1 parent 8155b6d commit 2039af0
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ public interface IPolicyRepository
IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, UseCaseId? useCase);
Task<(bool Exists, string LeftOperand, (AttributeKeyId? Key, IEnumerable<string> Values) Attributes, string? RightOperandValue)> GetPolicyContentAsync(UseCaseId? useCase, PolicyTypeId type, string credential);
IAsyncEnumerable<(string TechnicalKey, string LeftOperand, (AttributeKeyId? Key, IEnumerable<string> Values) Attributes, string? RightOperandValue)> GetPolicyForOperandContent(PolicyTypeId type, IEnumerable<string> technicalKeys);
Task<List<(string TechnicalKey, AttributeKeyId? AttributeKey, IEnumerable<string> Values)>> GetAttributeValuesForTechnicalKeys(PolicyTypeId type, IEnumerable<string> technicalKeys);
}
30 changes: 18 additions & 12 deletions src/database/PolicyHub.DbAccess/Repositories/PolicyRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,16 @@

namespace Org.Eclipse.TractusX.PolicyHub.DbAccess.Repositories;

public class PolicyRepository : IPolicyRepository
public class PolicyRepository(PolicyHubContext dbContext)
: IPolicyRepository
{
private readonly PolicyHubContext _dbContext;

public PolicyRepository(PolicyHubContext dbContext)
{
_dbContext = dbContext;
}

public IAsyncEnumerable<string> GetAttributeKeys() =>
_dbContext.AttributeKeys
dbContext.AttributeKeys
.Select(x => x.Label)
.AsAsyncEnumerable();

public IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, UseCaseId? useCase) =>
_dbContext.Policies
dbContext.Policies
.Where(p =>
(type == null || p.Types.Any(x => x.Id == type)) &&
(useCase == null || p.UseCases.Any(x => x.Id == useCase)))
Expand All @@ -54,7 +48,7 @@ public IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, U
.AsAsyncEnumerable();

public Task<(bool Exists, string LeftOperand, (AttributeKeyId? Key, IEnumerable<string> Values) Attributes, string? RightOperandValue)> GetPolicyContentAsync(UseCaseId? useCase, PolicyTypeId type, string credential) =>
_dbContext.Policies
dbContext.Policies
.Where(p =>
p.Types.Any(t => t.IsActive && t.Id == type) &&
(useCase == null || p.UseCases.Any(x => x.Id == useCase)) &&
Expand All @@ -68,7 +62,7 @@ public IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, U
.FirstOrDefaultAsync();

public IAsyncEnumerable<(string TechnicalKey, string LeftOperand, (AttributeKeyId? Key, IEnumerable<string> Values) Attributes, string? RightOperandValue)> GetPolicyForOperandContent(PolicyTypeId type, IEnumerable<string> technicalKeys) =>
_dbContext.Policies
dbContext.Policies
.Where(p =>
p.Types.Any(t => t.IsActive && t.Id == type) &&
technicalKeys.Contains(p.TechnicalKey))
Expand All @@ -79,4 +73,16 @@ public IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, U
p.PolicyKind!.Configuration!.RightOperandValue
))
.AsAsyncEnumerable();

public Task<List<(string TechnicalKey, AttributeKeyId? AttributeKey, IEnumerable<string> Values)>> GetAttributeValuesForTechnicalKeys(PolicyTypeId type, IEnumerable<string> technicalKeys) =>
dbContext.Policies
.Where(p =>
p.IsActive &&
p.Types.Any(t => t.IsActive && t.Id == type) &&
technicalKeys.Contains(p.TechnicalKey))
.Select(x => new ValueTuple<string, AttributeKeyId?, IEnumerable<string>>(
x.TechnicalKey,
x.AttributeKeyId,
x.Attributes.Select(a => a.AttributeValue)))
.ToListAsync();
}
28 changes: 2 additions & 26 deletions src/database/PolicyHub.Migrations/Seeder/Data/policies.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,31 +46,7 @@
"description": "With the Framework Credential, only those participants which have signed the respective framework agreement (general or via a specific version) are allowed to view or negotiate the respective data offer. Generic: \"rightOperand\": \"active\"; specific \"rightOperand\": \"active:{version}\"",
"is_active": true,
"attribute_key_id": 5
},
{
"id": "01a0fba3-9b6e-435a-b045-e0e890c300b7",
"kind_id": 4,
"technical_key": "purpose.trace.v1.TraceBattery",
"description": "Facilitating compliance with mandatory regulatory requirements for tracking and reporting battery cells, modules & high-voltage batteries.",
"is_active": true,
"attribute_key_id": 2
},
{
"id": "01a0fba3-9b6e-435a-b045-e0e890c300b8",
"kind_id": 4,
"technical_key": "purpose.trace.v1.aspects",
"description": "Establishing a digital representation of the automotive supply chain to enable a component specific data exchange.",
"is_active": true,
"attribute_key_id": 2
},
{
"id": "01a0fba3-9b6e-435a-b045-e0e890c300b9",
"kind_id": 4,
"technical_key": "purpose.trace.v1.qualityanalysis",
"description": " The data can be used for quality analysis to identify and select affected components and to send quality notifications to affected customers or suppliers.",
"is_active": true,
"attribute_key_id": 2
},
},
{
"id": "01a0fba3-9b6e-435a-b045-e0e890c300c1",
"kind_id": 5,
Expand All @@ -86,6 +62,6 @@
"technical_key": "purpose",
"description": "",
"is_active": true,
"attribute_key_id": 3
"attribute_key_id": 2
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b4", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b5", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b6", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b7", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b8", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b9", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c1", "policy_type_id": 1, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c1", "policy_type_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c2", "policy_type_id": 2, "is_active": true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b4", "use_case_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b5", "use_case_id": 3, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b6", "use_case_id": 4, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b7", "use_case_id": 1, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b8", "use_case_id": 1, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b9", "use_case_id": 1, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c1", "use_case_id": 1, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c1", "use_case_id": 2, "is_active": true },
{ "policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c1", "use_case_id": 3, "is_active": true },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@
"is_active": true
},
{
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b7",
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c2",
"key": 2,
"attribute_value": "purpose.trace.v1.TraceBattery",
"is_active": true
},
{
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b8",
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c2",
"key": 2,
"attribute_value": "purpose.trace.v1.aspects",
"is_active": true
},
{
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300b9",
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c2",
"key": 2,
"attribute_value": "purpose.trace.v1.qualityanalysis",
"is_active": true
Expand All @@ -82,11 +82,5 @@
"key": 4,
"attribute_value": "VW",
"is_active": true
},
{
"policy_id": "01a0fba3-9b6e-435a-b045-e0e890c300c2",
"key": 2,
"attribute_value": "ID Trace 3.1",
"is_active": true
}
]
45 changes: 32 additions & 13 deletions src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,18 @@

namespace Org.Eclipse.TractusX.PolicyHub.Service.BusinessLogic;

public class PolicyHubBusinessLogic : IPolicyHubBusinessLogic
public class PolicyHubBusinessLogic(IHubRepositories hubRepositories)
: IPolicyHubBusinessLogic
{
private readonly IHubRepositories _hubRepositories;

public PolicyHubBusinessLogic(IHubRepositories hubRepositories)
{
_hubRepositories = hubRepositories;
}

public IAsyncEnumerable<string> GetAttributeKeys() =>
_hubRepositories.GetInstance<IPolicyRepository>().GetAttributeKeys();
hubRepositories.GetInstance<IPolicyRepository>().GetAttributeKeys();

public IAsyncEnumerable<PolicyTypeResponse> GetPolicyTypes(PolicyTypeId? type, UseCaseId? useCase) =>
_hubRepositories.GetInstance<IPolicyRepository>().GetPolicyTypes(type, useCase);
hubRepositories.GetInstance<IPolicyRepository>().GetPolicyTypes(type, useCase);

public async Task<PolicyResponse> GetPolicyContentWithFiltersAsync(UseCaseId? useCase, PolicyTypeId type, string credential, OperatorId operatorId, string? value)
{
var (exists, leftOperand, attributes, rightOperandValue) = await _hubRepositories.GetInstance<IPolicyRepository>().GetPolicyContentAsync(useCase, type, credential).ConfigureAwait(false);
var (exists, leftOperand, attributes, rightOperandValue) = await hubRepositories.GetInstance<IPolicyRepository>().GetPolicyContentAsync(useCase, type, credential).ConfigureAwait(false);
if (!exists)
{
throw new NotFoundException($"Policy for type {type} and technicalKey {credential} does not exists");
Expand Down Expand Up @@ -126,10 +120,35 @@ public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest req
throw new ControllerArgumentException($"Keys {string.Join(",", multipleDefinedKey.Select(x => x.Key).Distinct())} have been defined multiple times");
}

var policies = await _hubRepositories.GetInstance<IPolicyRepository>().GetPolicyForOperandContent(requestData.PolicyType, requestData.Constraints.Select(x => x.Key)).ToListAsync().ConfigureAwait(false);
var technicalKeys = requestData.Constraints.Select(x => x.Key);
var attributeValuesForTechnicalKeys = await hubRepositories.GetInstance<IPolicyRepository>().GetAttributeValuesForTechnicalKeys(requestData.PolicyType, technicalKeys).ConfigureAwait(false);
if (technicalKeys.Except(attributeValuesForTechnicalKeys.Select(a => a.TechnicalKey)).Any())
{
throw new ControllerArgumentException($"Policy for type {requestData.PolicyType} and requested technicalKeys does not exists. TechnicalKeys {string.Join(",", attributeValuesForTechnicalKeys.Select(x => x.TechnicalKey))} are allowed");
}

IEnumerable<(string TechnicalKey, IEnumerable<string> Values)> keyValues = requestData.Constraints.GroupBy(x => x.Key).Select(x => new ValueTuple<string, IEnumerable<string>>(x.Key, x.Where(y => y.Value != null).Select(y => y.Value!)));
IEnumerable<(string TechnicalKey, IEnumerable<string> Values)> missingValues = keyValues
.Join(attributeValuesForTechnicalKeys, secondItem => secondItem.TechnicalKey, firstItem => firstItem.TechnicalKey,
(secondItem, firstItem) => new { secondItem, firstItem })
.Select(t => new { t, missing = t.secondItem.Values.Except(t.firstItem.Values) })
.Where(t => t.missing.Any())
.Select(t => (Key: t.t.secondItem.TechnicalKey, MissingValues: t.missing));

var attributesToIgnore = new[] { AttributeKeyId.Regex, AttributeKeyId.DynamicValue };
var technicalKeysToIgnore = attributeValuesForTechnicalKeys.Where(x => x.AttributeKey != null && attributesToIgnore.Any(y => y == x.AttributeKey)).Select(x => x.TechnicalKey);
var invalidValues = missingValues.Select(x => x.TechnicalKey).Except(technicalKeysToIgnore);
if (invalidValues.Any())
{
var x = missingValues.Where(x => invalidValues.Contains(x.TechnicalKey)).Select(x =>
$"Key: {x.TechnicalKey}, invalid values: {string.Join(',', x.Values)}");
throw new ControllerArgumentException($"Invalid values set for {string.Join(',', x)}");
}

var policies = await hubRepositories.GetInstance<IPolicyRepository>().GetPolicyForOperandContent(requestData.PolicyType, technicalKeys).ToListAsync().ConfigureAwait(false);
if (policies.Count != requestData.Constraints.Count())
{
throw new NotFoundException($"Policy for type {requestData.PolicyType} and technicalKeys {string.Join(",", requestData.Constraints.Select(x => x.Key).Except(policies.Select(x => x.TechnicalKey)))} does not exists");
throw new NotFoundException($"Policy for type {requestData.PolicyType} and technicalKeys {string.Join(",", technicalKeys.Except(policies.Select(x => x.TechnicalKey)))} does not exists");
}

var constraints = new List<Constraint>();
Expand Down
19 changes: 8 additions & 11 deletions tests/database/PolicyHub.DbAccess.Tests/PolicyRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,14 @@ public async Task GetPolicyTypes_ReturnsExpectedResult()
var result = await sut.GetPolicyTypes(null, null).ToListAsync();

// Assert
result.Should().NotBeEmpty().And.HaveCount(11).And.Satisfy(
result.Should().NotBeEmpty().And.HaveCount(8).And.Satisfy(
x => x.TechnicalKey == "BusinessPartnerNumber",
x => x.TechnicalKey == "Membership",
x => x.TechnicalKey == "FrameworkAgreement.traceability",
x => x.TechnicalKey == "FrameworkAgreement.quality",
x => x.TechnicalKey == "FrameworkAgreement.pcf",
x => x.TechnicalKey == "FrameworkAgreement.behavioraltwin",
x => x.TechnicalKey == "purpose.trace.v1.TraceBattery",
x => x.TechnicalKey == "purpose.trace.v1.aspects",
x => x.TechnicalKey == "companyRole.dismantler",
x => x.TechnicalKey == "purpose.trace.v1.qualityanalysis",
x => x.TechnicalKey == "purpose"
);
}
Expand Down Expand Up @@ -132,14 +129,14 @@ public async Task GetPolicyContentAsync_WithoutRightOperand_ReturnsExpectedResul
var sut = await CreateSut();

// Act
var result = await sut.GetPolicyContentAsync(null, PolicyTypeId.Usage, "purpose.trace.v1.TraceBattery");
var result = await sut.GetPolicyContentAsync(null, PolicyTypeId.Usage, "Membership");

// Assert
result.Exists.Should().BeTrue();
result.Attributes.Key.Should().Be(AttributeKeyId.Static);
result.Attributes.Values.Should().ContainSingle()
.And.Satisfy(x => x == "purpose.trace.v1.TraceBattery");
result.LeftOperand.Should().Be("purpose.trace.v1.TraceBattery");
.And.Satisfy(x => x == "active");
result.LeftOperand.Should().Be("Membership");
result.RightOperandValue.Should().BeNull();
}

Expand Down Expand Up @@ -172,15 +169,15 @@ public async Task GetPolicyForOperandContent__ReturnsExpectedResult()
var sut = await CreateSut();

// Act
var result = await sut.GetPolicyForOperandContent(PolicyTypeId.Usage, Enumerable.Repeat("purpose.trace.v1.TraceBattery", 1)).ToListAsync();
var result = await sut.GetPolicyForOperandContent(PolicyTypeId.Usage, Enumerable.Repeat("Membership", 1)).ToListAsync();

// Assert
result.Should().ContainSingle()
.And.Satisfy(
x => x.TechnicalKey == "purpose.trace.v1.TraceBattery" &&
x => x.TechnicalKey == "Membership" &&
x.Attributes.Key == AttributeKeyId.Static &&
x.Attributes.Values.Single() == "purpose.trace.v1.TraceBattery" &&
x.LeftOperand == "purpose.trace.v1.TraceBattery" &&
x.Attributes.Values.Single() == "active" &&
x.LeftOperand == "Membership" &&
x.RightOperandValue == null);
}

Expand Down
Loading

0 comments on commit 2039af0

Please sign in to comment.