Skip to content

Commit

Permalink
fix(errorHandling): adjust general error handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Phil91 committed Feb 12, 2025
1 parent 60bfb13 commit 808b082
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/database/PolicyHub.DbAccess/IHubRepositories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ namespace Org.Eclipse.TractusX.PolicyHub.DbAccess;

public interface IHubRepositories
{
public T GetInstance<T>();
T GetInstance<T>();
}
4 changes: 2 additions & 2 deletions src/database/PolicyHub.DbAccess/PolicyHub.DbAccess.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.DependencyInjection" Version="3.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling" Version="3.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.DependencyInjection" Version="3.5.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling" Version="3.5.0" />
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions src/database/PolicyHub.Migrations/PolicyHub.Migrations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.1" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.DBAccess" Version="3.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.Logging" Version="3.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.Seeding" Version="3.3.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.DBAccess" Version="3.5.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.Logging" Version="3.5.0" />
<PackageReference Include="Org.Eclipse.TractusX.Portal.Backend.Framework.Seeding" Version="3.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
41 changes: 21 additions & 20 deletions src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Org.Eclipse.TractusX.PolicyHub.DbAccess.Models;
using Org.Eclipse.TractusX.PolicyHub.DbAccess.Repositories;
using Org.Eclipse.TractusX.PolicyHub.Entities.Enums;
using Org.Eclipse.TractusX.PolicyHub.Service.ErrorHandling;
using Org.Eclipse.TractusX.PolicyHub.Service.Extensions;
using Org.Eclipse.TractusX.PolicyHub.Service.Models;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
Expand All @@ -42,13 +43,13 @@ public async Task<PolicyResponse> GetPolicyContentWithFiltersAsync(UseCaseId? us
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");
throw NotFoundException.Create(PolicyErrors.POLICY_NOT_EXIST, new ErrorParameter[] { new("type", type.ToString()), new("credential", credential) });
}

var rightOperands = attributes.Values.Select(a => rightOperandValue != null ? string.Format(rightOperandValue, a) : a);
if (attributes.Key == null && rightOperandValue == null)
{
throw new UnexpectedConditionException("There must be one configured rightOperand value");
throw UnexpectedConditionException.Create(PolicyErrors.NO_RIGHT_OPERAND_CONFIGURED);
}

var (rightOperand, additionalAttribute) = attributes.Key != null ?
Expand All @@ -64,18 +65,19 @@ private static (object rightOperand, AdditionalAttributes? additionalAttribute)
AttributeKeyId.DynamicValue => (value ?? "{dynamicValue}", null),
AttributeKeyId.Regex => (GetRegexValue(attributes, value), null),
_ => operatorId == OperatorId.Equals
? processEqualsOperator(attributes, rightOperands, value, leftOperand, useCase)
? ProcessEqualsOperator(attributes, rightOperands, value, leftOperand, useCase)
: (rightOperands, null)
};

private static (object rightOperand, AdditionalAttributes? additionalAttribute) processEqualsOperator((AttributeKeyId? Key, IEnumerable<string> Values) attributes, IEnumerable<string> rightOperands, string? value, string leftOperand, UseCaseId? useCase)
private static (object rightOperand, AdditionalAttributes? additionalAttribute) ProcessEqualsOperator((AttributeKeyId? Key, IEnumerable<string> Values) attributes, IEnumerable<string> rightOperands, string? value, string leftOperand, UseCaseId? useCase)
{
if (value != null)
{
if (!rightOperands.Any(r => r == value))
{
throw new ControllerArgumentException($"Invalid values [{value}] set for key {leftOperand}. Possible values [{string.Join(",", rightOperands)}]");
throw ControllerArgumentException.Create(PolicyErrors.INVALID_VALUES, new ErrorParameter[] { new("value", value), new("leftOperand", leftOperand), new("possibleValues", string.Join(",", rightOperands)) });
}

rightOperands = rightOperands.Where(r => r.Equals(value));
}

Expand All @@ -92,17 +94,17 @@ private static object GetRegexValue((AttributeKeyId? Key, IEnumerable<string> Va
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ControllerArgumentException("you must provide a value for the regex", nameof(value));
throw ControllerArgumentException.Create(PolicyErrors.NO_VALUE_FOR_REGEX);
}

if (attributes.Values.Count() != 1)
{
throw new UnexpectedConditionException("There should only be one regex pattern defined");
throw UnexpectedConditionException.Create(PolicyErrors.MULTIPLE_REGEX_DEFINED);
}

if (!Regex.IsMatch(value, attributes.Values.Single(), RegexOptions.Compiled, TimeSpan.FromSeconds(1)))
{
throw new ControllerArgumentException($"The provided value {value} does not match the regex pattern {attributes.Values.Single()}", nameof(value));
throw ControllerArgumentException.Create(PolicyErrors.VALUE_DOES_NOT_MATCH_REGEX, new ErrorParameter[] { new("value", value), new("values", attributes.Values.Single()) });
}

return value;
Expand All @@ -126,24 +128,24 @@ private static PolicyFileContent CreateFileContent(PolicyTypeId type, OperatorId

public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest requestData)

Check warning on line 129 in src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs

View workflow job for this annotation

GitHub Actions / Build (9.0)

Refactor this method to reduce its Cognitive Complexity from 43 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
if (requestData.PolicyType == PolicyTypeId.Usage && requestData.ConstraintOperand == ConstraintOperandId.Or)
if (requestData is { PolicyType: PolicyTypeId.Usage, ConstraintOperand: ConstraintOperandId.Or })
{
throw new ControllerArgumentException($"The support of OR constraintOperand for Usage constraints are not supported for now");
throw ControllerArgumentException.Create(PolicyErrors.OR_WITH_USAGE);
}

if (requestData.PolicyType == PolicyTypeId.Access && requestData.ConstraintOperand == ConstraintOperandId.And && requestData.Constraints.Any(x => x.Key == "BusinessPartnerNumber" && (x.Value!.Split(",").Count() > 1)))
{
throw new ControllerArgumentException($"Only a single value BPNL is allowed with an AND constraint");
throw ControllerArgumentException.Create(PolicyErrors.SINGLE_VALUE_BPNL_CONSTRAINT);
}

if (requestData.Constraints.Any(x => x.Key == "BusinessPartnerNumber") && !requestData.Constraints.Any(x => x.Operator == OperatorId.Equals))
{
throw new ControllerArgumentException($"The operator for BPNLs should always be Equals");
throw ControllerArgumentException.Create(PolicyErrors.BPNL_WRONG_OPERATOR);
}

if (requestData.PolicyType == PolicyTypeId.Usage && requestData.Constraints.Any(x => x.Key == "BusinessPartnerNumber" && (x.Value!.Split(",").Count() > 1)))
if (requestData.PolicyType == PolicyTypeId.Usage && requestData.Constraints.Any(x => x.Key == "BusinessPartnerNumber" && x.Value!.Split(",").Length > 1))
{
throw new ControllerArgumentException($"For usage policies only a single BPNL is allowed");
throw ControllerArgumentException.Create(PolicyErrors.USAGE_MULTIPLE_BPNL);
}

var keyCounts = requestData.Constraints
Expand All @@ -152,14 +154,14 @@ public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest req
var multipleDefinedKey = keyCounts.Where(x => x.Value != 1);
if (multipleDefinedKey.Any())
{
throw new ControllerArgumentException($"Keys {string.Join(",", multipleDefinedKey.Select(x => x.Key).Distinct())} have been defined multiple times");
throw ControllerArgumentException.Create(PolicyErrors.KEY_DEFINED_MULTIPLE_TIMES, new ErrorParameter[] { new("keys", string.Join(",", multipleDefinedKey.Select(x => x.Key).Distinct())) });
}

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");
throw ControllerArgumentException.Create(PolicyErrors.POLICY_NOT_EXISTS_FOR_TECHNICAL_KEYS, new ErrorParameter[] { new("policyType", requestData.PolicyType.ToString()), new("technicalKeys", string.Join(",", attributeValuesForTechnicalKeys.Select(x => x.TechnicalKey))) });
}

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).SelectMany(y => y.Value!.Split(","))));
Expand All @@ -177,13 +179,13 @@ public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest req
{
var x = missingValues.Where(x => invalidValues.Contains(x.TechnicalKey)).Select(x =>
$"Key: {x.TechnicalKey}, requested value[{string.Join(',', x.Values)}] Possible Values[{string.Join(',', attributeValuesForTechnicalKeys.Where(a => a.TechnicalKey.Equals(x.TechnicalKey)).Select(a => a.Values).First())}]");
throw new ControllerArgumentException($"Invalid values set for {string.Join(',', x)}");
throw ControllerArgumentException.Create(PolicyErrors.INVALID_VALUES_SET, new ErrorParameter[] { new("values", 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(",", technicalKeys.Except(policies.Select(x => x.TechnicalKey)))} does not exists");
throw NotFoundException.Create(PolicyErrors.POLICY_NOT_EXISTS_FOR_TECHNICAL_KEYS, new ErrorParameter[] { new("policyType", requestData.PolicyType.ToString()), new("technicalKeys", string.Join(",", technicalKeys.Except(policies.Select(x => x.TechnicalKey)))) });
}

var constraints = new List<Constraint>();
Expand All @@ -194,7 +196,7 @@ public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest req
var rightOperands = policy.Attributes.Values.Select(a => policy.RightOperandValue != null ? string.Format(policy.RightOperandValue, a) : a);
if (policy.Attributes.Key == null && policy.RightOperandValue == null)
{
throw new UnexpectedConditionException("There must be one configured rightOperand value");
throw UnexpectedConditionException.Create(PolicyErrors.RIGHT_OPERAND_NOT_CONFIGURED);
}

if (constraint.Value != null)
Expand All @@ -216,7 +218,6 @@ public async Task<PolicyResponse> GetPolicyContentAsync(PolicyContentRequest req
constraint.Operator.OperatorToJsonString(),
rightOperand
));

}
}
else
Expand Down
64 changes: 64 additions & 0 deletions src/hub/PolicyHub.Service/ErrorHandling/ErrorMessageContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/********************************************************************************
* Copyright (c) 2025 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service;
using System.Collections.Immutable;

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

public class ErrorMessageContainer : IErrorMessageContainer
{
private static readonly IReadOnlyDictionary<int, string> Messages = ImmutableDictionary.CreateRange<int, string>([
new((int)PolicyErrors.POLICY_NOT_EXIST, "Policy for type {type} and technicalKey {credential} does not exists"),
new((int)PolicyErrors.NO_RIGHT_OPERAND_CONFIGURED, "There must be one configured rightOperand value"),
new((int)PolicyErrors.INVALID_VALUES, "Invalid values [{value}] set for key {leftOperand}. Possible values [{possibleValues}]"),
new((int)PolicyErrors.NO_VALUE_FOR_REGEX, "you must provide a value for the regex"),
new((int)PolicyErrors.MULTIPLE_REGEX_DEFINED, "There should only be one regex pattern defined"),
new((int)PolicyErrors.VALUE_DOES_NOT_MATCH_REGEX, "The provided value {value} does not match the regex pattern {values}"),
new((int)PolicyErrors.OR_WITH_USAGE, "The support of OR constraintOperand for Usage constraints are not supported for now"),
new((int)PolicyErrors.SINGLE_VALUE_BPNL_CONSTRAINT, "Only a single value BPNL is allowed with an AND constraint"),
new((int)PolicyErrors.BPNL_WRONG_OPERATOR, "The operator for BPNLs should always be Equals"),
new((int)PolicyErrors.USAGE_MULTIPLE_BPNL, "For usage policies only a single BPNL is allowed"),
new((int)PolicyErrors.KEY_DEFINED_MULTIPLE_TIMES, "Keys {keys} have been defined multiple times"),
new((int)PolicyErrors.POLICY_NOT_EXISTS_FOR_TECHNICAL_KEYS, "Policy for type {policyType} and requested technicalKeys does not exists. TechnicalKeys {technicalKeys} are allowed"),
new((int)PolicyErrors.INVALID_VALUES_SET, "Invalid values set for {values}"),
new((int)PolicyErrors.RIGHT_OPERAND_NOT_CONFIGURED, "There must be one configured rightOperand value")
]);

public Type Type { get => typeof(PolicyErrors); }
public IReadOnlyDictionary<int, string> MessageContainer { get => Messages; }
}

public enum PolicyErrors
{
POLICY_NOT_EXIST,
NO_RIGHT_OPERAND_CONFIGURED,
INVALID_VALUES,
NO_VALUE_FOR_REGEX,
MULTIPLE_REGEX_DEFINED,
VALUE_DOES_NOT_MATCH_REGEX,
OR_WITH_USAGE,
SINGLE_VALUE_BPNL_CONSTRAINT,
BPNL_WRONG_OPERATOR,
USAGE_MULTIPLE_BPNL,
KEY_DEFINED_MULTIPLE_TIMES,
POLICY_NOT_EXISTS_FOR_TECHNICAL_KEYS,
INVALID_VALUES_SET,
RIGHT_OPERAND_NOT_CONFIGURED
}
Loading

0 comments on commit 808b082

Please # to comment.