Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 5 additions & 8 deletions docs/api/hub-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ paths:
in: query
description: 'OPTIONAL: Type to filter the response'
schema:
$ref: '#/components/schemas/PolicyTypeId'
type: string
- name: useCase
in: query
description: 'OPTIONAL: UseCase to filter the response'
schema:
$ref: '#/components/schemas/UseCaseId'
type: string
responses:
'200':
description: OK
Expand Down Expand Up @@ -75,25 +75,22 @@ paths:
in: query
description: 'OPTIONAL: The use case'
schema:
$ref: '#/components/schemas/UseCaseId'
type: string
- name: type
in: query
description: 'Policy type for which the policy is supposed to get created. Possible values: ''Access'' or ''Usage'''
required: true
schema:
$ref: '#/components/schemas/PolicyTypeId'
type: string
- name: policyName
in: query
description: The technical key of the policy
required: true
schema:
type: string
- name: operatorType
in: query
description: 'Policy Rule operator. Possible values: ''Equals'' or ''In'''
required: true
schema:
$ref: '#/components/schemas/OperatorId'
type: string
- name: value
in: query
description: 'OPTIONAL: Value to be used for the rightOperand'
Expand Down
21 changes: 10 additions & 11 deletions src/hub/PolicyHub.Service/BusinessLogic/PolicyHubBusinessLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,27 @@ 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 ControllerArgumentException.Create(PolicyErrors.INVALID_VALUES, new ErrorParameter[] { new("value", value), new("leftOperand", leftOperand), new("possibleValues", string.Join(",", rightOperands)) });
throw new ControllerArgumentException($"Invalid values [{value}] set for key {leftOperand}. Possible values [{string.Join(",", rightOperands)}]");
}

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

var useCaseValue = useCase != null ?
useCase.Value.ToString().Insert(0, ".") :
string.Empty;
var rightOperand = $"@{leftOperand}{useCaseValue}-{attributes.Key}";
return rightOperands.Count() > 1 ?
(rightOperand, new AdditionalAttributes(rightOperand, rightOperands)) :
($"@{leftOperand}{(useCase != null ?
useCase.Value.ToString().Insert(0, ".") :
string.Empty)}-{attributes.Key}",
new AdditionalAttributes($"@{leftOperand}{(useCase != null ?
useCase.Value.ToString().Insert(0, ".") :
string.Empty)}-{attributes.Key}", rightOperands)) :
(rightOperands.Single(), null);
}

Expand Down Expand Up @@ -180,7 +179,7 @@ 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 ControllerArgumentException.Create(PolicyErrors.INVALID_VALUES_SET, new ErrorParameter[] { new("values", string.Join(',', x)) });
throw new ControllerArgumentException($"Invalid values set for {string.Join(',', x)}");
}

var policies = await hubRepositories.GetInstance<IPolicyRepository>().GetPolicyForOperandContent(requestData.PolicyType, technicalKeys).ToListAsync().ConfigureAwait(false);
Expand Down
24 changes: 18 additions & 6 deletions src/hub/PolicyHub.Service/Controllers/PolicyHubController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using Org.Eclipse.TractusX.PolicyHub.Entities.Enums;
using Org.Eclipse.TractusX.PolicyHub.Service.BusinessLogic;
using Org.Eclipse.TractusX.PolicyHub.Service.Extensions;
using Org.Eclipse.TractusX.PolicyHub.Service.Filters;
using Org.Eclipse.TractusX.PolicyHub.Service.Models;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Web;
Expand All @@ -46,7 +47,9 @@ public static RouteGroupBuilder MapPolicyHubApi(this RouteGroupBuilder group)
.WithDefaultResponses()
.Produces(StatusCodes.Status200OK, typeof(string), Constants.JsonContentType);

policyHub.MapGet("policy-types", (PolicyTypeId? type, UseCaseId? useCase, IPolicyHubBusinessLogic logic) => logic.GetPolicyTypes(type, useCase))
policyHub.MapGet("policy-types", (string? type, string? useCase, IPolicyHubBusinessLogic logic) =>
logic.GetPolicyTypes(ParamEnumHelper.ParseEnum<PolicyTypeId>(type), ParamEnumHelper.ParseEnum<UseCaseId>(useCase)))
.AddEndpointFilter<PolicyTypesQueryParametersFilter>()
.WithSwaggerDescription("Provides all current supported policy types incl. a policy description and useCase link.",
"Example: GET: api/policy-hub/policy-types",
"OPTIONAL: Type to filter the response",
Expand All @@ -56,12 +59,21 @@ public static RouteGroupBuilder MapPolicyHubApi(this RouteGroupBuilder group)
.Produces(StatusCodes.Status200OK, typeof(PolicyTypeResponse), Constants.JsonContentType);

policyHub.MapGet("policy-content",
(UseCaseId? useCase,
PolicyTypeId type,
string policyName,
OperatorId operatorType,
(string? useCase,
String? type,
string? policyName,
String? operatorType,
string? value,
IPolicyHubBusinessLogic logic) => logic.GetPolicyContentWithFiltersAsync(useCase, type, policyName, operatorType, value))
IPolicyHubBusinessLogic logic) =>
{
if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(policyName) || string.IsNullOrEmpty(operatorType))
{
throw new ArgumentNullException(nameof(type), "Type parameter cannot be null or empty.");
}

return logic.GetPolicyContentWithFiltersAsync(ParamEnumHelper.ParseEnum<UseCaseId>(useCase), Enum.Parse<PolicyTypeId>(type), policyName, Enum.Parse<OperatorId>(operatorType), value);
})
.AddEndpointFilter<PolicyContentQueryParametersFilter>()
.WithSwaggerDescription("Receive the policy template 'access' or 'usage' for a single policy rule based on the request parameters submitted by the user.",
"Example: GET: api/policy-hub/policy-content",
"OPTIONAL: The use case",
Expand Down
93 changes: 93 additions & 0 deletions src/hub/PolicyHub.Service/Filters/BaseQueryParametersFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/********************************************************************************
* 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;

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

public abstract class BaseQueryParametersFilter : IEndpointFilter
{
protected readonly Dictionary<string, QueryParameterType> _queryParameters;

protected BaseQueryParametersFilter(Dictionary<string, QueryParameterType> queryParameters)
{
_queryParameters = queryParameters;
}

public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
var queryParams = context.HttpContext.Request.Query;

// Check for required parameters
EnsureRequiredParameters(queryParams);

// Validate supported query parameters
ValidateSupportedQueryParameters(queryParams);

// Validate enum parameter values
ValidateEnumParameters(queryParams);

// Continue with the next filter or action
return await next(context);
}

private void EnsureRequiredParameters(IQueryCollection queryParams)
{
var missingRequiredParameters = _queryParameters
.Where(q => q.Value.IsRequired && GetArgument(queryParams, q.Key) == null)
.Select(q => q.Key)
.ToList();

if (missingRequiredParameters.Count != 0)
{
throw new NotFoundException($"Missing required parameters: {string.Join(", ", missingRequiredParameters)}.");
}
}

private void ValidateSupportedQueryParameters(IQueryCollection queryParams)
{
var invalidParameters = queryParams
.Where(q => !_queryParameters.ContainsKey(q.Key))
.Select(q => $"{q.Key}")
.ToList();

if (invalidParameters.Count != 0)
{
throw new ControllerArgumentException($"Invalid query parameters: {string.Join(", ", invalidParameters)}. Supported parameters are: {string.Join(", ", _queryParameters.Keys)}.");
}
}

private void ValidateEnumParameters(IQueryCollection queryParams)
{
foreach (var param in _queryParameters)
{
var paramValue = GetArgument(queryParams, param.Key);
if (paramValue != null && param.Value.EnumType != null)
{
ParamEnumValidator.ThrowIfInvalidEnumValue(param.Value.EnumType, paramValue, param.Key);
}
}
}

protected static string? GetArgument(IQueryCollection queryParams, string argumentName)
{
return queryParams.TryGetValue(argumentName, out var value) ? value.ToString() : null;
}
}

33 changes: 33 additions & 0 deletions src/hub/PolicyHub.Service/Filters/ParamEnumHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/********************************************************************************
* 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
********************************************************************************/

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

public static class ParamEnumHelper
{
public static TEnum? ParseEnum<TEnum>(string? value) where TEnum : struct, Enum
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}

return Enum.Parse<TEnum>(value.Trim(), true);
}
}
34 changes: 34 additions & 0 deletions src/hub/PolicyHub.Service/Filters/ParamEnumValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/********************************************************************************
* 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;

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

public static class ParamEnumValidator
{
public static void ThrowIfInvalidEnumValue(Type enumType, string value, string paramName)
{
if (!Enum.IsDefined(enumType, value))
{
var acceptedValues = string.Join(", ", Enum.GetNames(enumType));
throw new ControllerArgumentException($"Invalid value '{value}' for parameter '{paramName}'. Accepted values are: {acceptedValues}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/********************************************************************************
* 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.PolicyHub.Entities.Enums;

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

public class PolicyContentQueryParametersFilter : BaseQueryParametersFilter
{
public PolicyContentQueryParametersFilter() : base(new Dictionary<string, QueryParameterType>
{
{ "useCase", new QueryParameterType { IsRequired = false, EnumType = typeof(UseCaseId) } },
{ "type", new QueryParameterType { IsRequired = true, EnumType = typeof(PolicyTypeId) } },
{ "policyName", new QueryParameterType { IsRequired = true } },
{ "operatorType", new QueryParameterType { IsRequired = true, EnumType = typeof(OperatorId) } },
{ "value", new QueryParameterType { IsRequired = false } }
})
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/********************************************************************************
* 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.PolicyHub.Entities.Enums;

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

public class PolicyTypesQueryParametersFilter : BaseQueryParametersFilter
{
public PolicyTypesQueryParametersFilter() : base(new Dictionary<string, QueryParameterType>
{
{ "useCase", new QueryParameterType { IsRequired = false, EnumType = typeof(UseCaseId) } },
{ "type", new QueryParameterType { IsRequired = false, EnumType = typeof(PolicyTypeId) } }
})
{
}
}
Loading