Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(appRelease): created an endpoint to fetch user roles for an app provider #633

Merged
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
60 changes: 60 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
###############################################################
# Copyright (c) 2024 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
###############################################################

---
version: 2
updates:
# NuGet
-
package-ecosystem: "nuget"
target-branch: dev
directory: /
labels:
- "dependabot"
- "dependencies"
schedule:
interval: "weekly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]

# Github Actions
-
package-ecosystem: "github-actions"
target-branch: dev
directory: /
labels:
- "dependabot"
- "github-actions"
schedule:
interval: "weekly"

# Docker
-
package-ecosystem: "docker"
target-branch: dev
directory: ./docker/
labels:
- "dependabot"
- "docker"
schedule:
interval: "weekly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ public async Task CreateActiveAppDocumentAsync(Guid appId, DocumentTypeId docume
/// <inheritdoc />
public async Task<IEnumerable<ActiveAppRoleDetails>> GetActiveAppRolesAsync(Guid appId, string? languageShortName)
{
var (isValid, isActive, roleDetails) = await _portalRepositories.GetInstance<IUserRolesRepository>().GetActiveAppRolesAsync(appId, OfferTypeId.APP, languageShortName, Constants.DefaultLanguage).ConfigureAwait(ConfigureAwaitOptions.None);
var (isValid, isActive, roleDetails) = await _portalRepositories.GetInstance<IUserRolesRepository>().GetActiveOfferRolesAsync(appId, OfferTypeId.APP, languageShortName, Constants.DefaultLanguage).ConfigureAwait(ConfigureAwaitOptions.None);
if (!isValid)
{
throw new NotFoundException($"App {appId} does not exist");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,4 +556,19 @@ public Task<IEnumerable<TechnicalUserProfileInformation>> GetTechnicalUserProfil
/// <inheritdoc />
public Task UpdateTechnicalUserProfiles(Guid appId, IEnumerable<TechnicalUserProfileData> data) =>
_offerService.UpdateTechnicalUserProfiles(appId, OfferTypeId.APP, data, _settings.TechnicalUserProfileClient);

/// <inheritdoc />
public async Task<IEnumerable<ActiveAppRoleDetails>> GetAppProviderRolesAsync(Guid appId, string? languageShortName)
{
var (isValid, isProvider, roleDetails) = await _portalRepositories.GetInstance<IUserRolesRepository>().GetOfferProviderRolesAsync(appId, OfferTypeId.APP, _identityData.CompanyId, languageShortName, Constants.DefaultLanguage).ConfigureAwait(ConfigureAwaitOptions.None);
if (!isValid)
{
throw new NotFoundException($"App {appId} does not exist");
}
if (!isProvider)
{
throw new ForbiddenException($"Company {_identityData.CompanyId} is not the provider company");
}
return roleDetails ?? throw new UnexpectedConditionException("roleDetails should never be null here");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,11 @@ public interface IAppReleaseBusinessLogic
/// <param name="appId">Id of the app</param>
/// <param name="data">The technical user profiles</param>
Task UpdateTechnicalUserProfiles(Guid appId, IEnumerable<TechnicalUserProfileData> data);

/// <summary>
/// Get an user roles for an app provider
/// </summary>
/// <param name="appId">Id of the app</param>
/// <param name="languageShortName"></param>
Task<IEnumerable<ActiveAppRoleDetails>> GetAppProviderRolesAsync(Guid appId, string? languageShortName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,22 @@ public async Task<NoContentResult> CreateAndUpdateTechnicalUserProfiles([FromRou
await _appReleaseBusinessLogic.UpdateTechnicalUserProfiles(appId, data).ConfigureAwait(ConfigureAwaitOptions.None);
return NoContent();
}

/// <summary>
/// Gets the app providers an overview of configures app roles.
/// </summary>
/// <param name="appId" example="D3B1ECA2-6148-4008-9E6C-C1C2AEA5C645">Id of the app which roles should be returned.</param>
/// <param name="languageShortName">OPTIONAL: The language short name.</param>
/// <returns>Returns the app providers an overview of configures app roles.</returns>
/// <remarks>Example: GET: /api/apps/AppChange/D3B1ECA2-6148-4008-9E6C-C1C2AEA5C645/roles</remarks>
/// <response code="200">Returns the client roles.</response>
/// <response code="404">The app was not found.</response>
/// <response code="403">The app is not the provider of the company</response>
[HttpGet]
[Authorize(Roles = "view_client_roles")]
[Authorize(Policy = PolicyTypes.ValidCompany)]
[Route("{appId}/roles")]
[ProducesResponseType(typeof(IEnumerable<ActiveAppRoleDetails>), StatusCodes.Status200OK)]
public Task<IEnumerable<ActiveAppRoleDetails>> GetAppProviderRolesAsync([FromRoute] Guid appId, [FromQuery] string? languageShortName = null) =>
_appReleaseBusinessLogic.GetAppProviderRolesAsync(appId, languageShortName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,13 @@ public interface IUserRolesRepository
/// <param name="offerId"></param>
/// <param name="languageShortName"></param>
/// <returns></returns>
Task<(bool IsValid, bool IsActive, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetActiveAppRolesAsync(Guid offerId, OfferTypeId offerTypeId, string? languageShortName, string defaultLanguageShortName);
Task<(bool IsValid, bool IsActive, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetActiveOfferRolesAsync(Guid offerId, OfferTypeId offerTypeId, string? languageShortName, string defaultLanguageShortName);

/// <summary>
/// Gets userRoles for an app provider
/// </summary>
/// <param name="offerId"></param>
/// <param name="languageShortName"></param>
/// <returns></returns>
Task<(bool IsValid, bool IsProvider, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetOfferProviderRolesAsync(Guid offerId, OfferTypeId offerTypeId, Guid companyId, string? languageShortName, string defaultLanguageShortName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ public IAsyncEnumerable<Guid> GetRolesForClient(string technicalUserProfileClien
.ToAsyncEnumerable();

/// <inheritdoc />
public Task<(bool IsValid, bool IsActive, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetActiveAppRolesAsync(Guid offerId, OfferTypeId offerTypeId, string? languageShortName, string defaultLanguageShortName) =>
public Task<(bool IsValid, bool IsActive, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetActiveOfferRolesAsync(Guid offerId, OfferTypeId offerTypeId, string? languageShortName, string defaultLanguageShortName) =>
_dbContext.Offers
.AsNoTracking()
.Where(offer => offer!.Id == offerId && offer.OfferTypeId == offerTypeId)
Expand All @@ -301,4 +301,30 @@ public IAsyncEnumerable<Guid> GetRolesForClient(string technicalUserProfileClien
description.Description))))
: null))
.SingleOrDefaultAsync();

/// <inheritdoc />
public Task<(bool IsValid, bool IsProvider, IEnumerable<ActiveAppRoleDetails>? AppRoleDetails)> GetOfferProviderRolesAsync(Guid offerId, OfferTypeId offerTypeId, Guid companyId, string? languageShortName, string defaultLanguageShortName) =>
_dbContext.Offers
.AsNoTracking()
.Where(offer => offer!.Id == offerId && offer.OfferTypeId == offerTypeId)
.Select(offer => new
{
Provider = offer.ProviderCompanyId == companyId,
Roles = offer.UserRoles
})
.Select(x => new ValueTuple<bool, bool, IEnumerable<ActiveAppRoleDetails>?>(
true,
x.Provider,
x.Provider
? x.Roles.Select(role =>
new ActiveAppRoleDetails(
role.UserRoleText,
role.UserRoleDescriptions.Where(description =>
(languageShortName != null && description.LanguageShortName == languageShortName) ||
description.LanguageShortName == defaultLanguageShortName)
.Select(description => new ActiveAppUserRoleDescription(
description.LanguageShortName,
description.Description))))
: null))
.SingleOrDefaultAsync();
}
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ public async Task GetActiveAppRolesAsync_Throws_NotFoundException()
// Arrange
var appId = _fixture.Create<Guid>();
var activeAppRoleDetails = default((bool, bool, IEnumerable<ActiveAppRoleDetails>));
A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
Expand All @@ -1164,7 +1164,7 @@ public async Task GetActiveAppRolesAsync_Throws_NotFoundException()
// Assert
var result = await Assert.ThrowsAsync<NotFoundException>(Act);
result.Message.Should().Be($"App {appId} does not exist");
A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(appId, OfferTypeId.APP, null, "en"))
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(appId, OfferTypeId.APP, null, "en"))
.MustHaveHappenedOnceExactly();
}

Expand All @@ -1173,8 +1173,8 @@ public async Task GetActiveAppRolesAsync_Throws_ConflictException()
{
// Arrange
var appId = _fixture.Create<Guid>();
var activeAppRoleDetails = (true, false, _fixture.CreateMany<ActiveAppRoleDetails>());
A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
var activeAppRoleDetails = (true, false, default(IEnumerable<ActiveAppRoleDetails>?));
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
Expand All @@ -1183,7 +1183,7 @@ public async Task GetActiveAppRolesAsync_Throws_ConflictException()
// Assert
var result = await Assert.ThrowsAsync<ConflictException>(Act);
result.Message.Should().Be($"App {appId} is not Active");
A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(appId, OfferTypeId.APP, "de", "en"))
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(appId, OfferTypeId.APP, "de", "en"))
.MustHaveHappenedOnceExactly();
}

Expand All @@ -1203,7 +1203,7 @@ public async Task GetActiveAppRolesAsync_ReturnsExpected()
userRole2
});

A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(A<Guid>._, A<OfferTypeId>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
Expand All @@ -1214,7 +1214,7 @@ public async Task GetActiveAppRolesAsync_ReturnsExpected()
.And.Satisfy(
x => x.Role == "TestRole1" && x.Descriptions.Count() == 1 && x.Descriptions.Single().Description == "TestRole1 description",
x => x.Role == "TestRole2" && x.Descriptions.Count() == 1 && x.Descriptions.Single().Description == "TestRole2 description");
A.CallTo(() => _userRolesRepository.GetActiveAppRolesAsync(appId, OfferTypeId.APP, "de", "en"))
A.CallTo(() => _userRolesRepository.GetActiveOfferRolesAsync(appId, OfferTypeId.APP, "de", "en"))
.MustHaveHappenedOnceExactly();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,79 @@ public async Task GetTechnicalUserProfilesForOffer_ReturnsExpected()

#endregion

#region GetAppProviderRoles

[Fact]
public async Task GetAppProviderRolesAsync_Throws_NotFoundException()
{
// Arrange
var appId = _fixture.Create<Guid>();
var activeAppRoleDetails = default((bool, bool, IEnumerable<ActiveAppRoleDetails>));
A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(A<Guid>._, A<OfferTypeId>._, A<Guid>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
Task Act() => _sut.GetAppProviderRolesAsync(appId, null);

// Assert
var result = await Assert.ThrowsAsync<NotFoundException>(Act);
result.Message.Should().Be($"App {appId} does not exist");
A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(appId, OfferTypeId.APP, _identity.CompanyId, null, "en"))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task GetAppProviderRolesAsync_Throws_ForbiddenException()
{
// Arrange
var appId = _fixture.Create<Guid>();
var activeAppRoleDetails = (true, false, default(IEnumerable<ActiveAppRoleDetails>?));
A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(A<Guid>._, A<OfferTypeId>._, A<Guid>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
Task Act() => _sut.GetAppProviderRolesAsync(appId, "de");

// Assert
var result = await Assert.ThrowsAsync<ForbiddenException>(Act);
result.Message.Should().Be($"Company {_identity.CompanyId} is not the provider company");
A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(appId, OfferTypeId.APP, _identity.CompanyId, "de", "en"))
.MustHaveHappenedOnceExactly();
}

[Fact]
public async Task GetAppProviderRolesAsync_ReturnsExpected()
{
// Arrange
var appId = _fixture.Create<Guid>();
var userRole1 = new ActiveAppRoleDetails("TestRole1", [
new ActiveAppUserRoleDescription("en", "TestRole1 description")
]);
var userRole2 = new ActiveAppRoleDetails("TestRole2", [
new ActiveAppUserRoleDescription("en", "TestRole2 description")
]);
var activeAppRoleDetails = (true, true, new[] {
userRole1,
userRole2
});

A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(A<Guid>._, A<OfferTypeId>._, A<Guid>._, A<string>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
var result = await _sut.GetAppProviderRolesAsync(appId, "de");

// Assert
result.Should().HaveCount(2)
.And.Satisfy(
x => x.Role == "TestRole1" && x.Descriptions.Count() == 1 && x.Descriptions.Single().Description == "TestRole1 description",
x => x.Role == "TestRole2" && x.Descriptions.Count() == 1 && x.Descriptions.Single().Description == "TestRole2 description");
A.CallTo(() => _userRolesRepository.GetOfferProviderRolesAsync(appId, OfferTypeId.APP, _identity.CompanyId, "de", "en"))
.MustHaveHappenedOnceExactly();
}

#endregion

#region Setup

private void SetupUpdateApp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities;
using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared;
using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions;
using System.Collections.Immutable;
using Xunit;

namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.Controllers.Tests;
Expand Down Expand Up @@ -403,4 +404,25 @@ public async Task UpdateTechnicalUserProfiles_ReturnsExpectedCount()
A.CallTo(() => _logic.UpdateTechnicalUserProfiles(offerId, A<IEnumerable<TechnicalUserProfileData>>.That.Matches(x => x.Count() == 5))).MustHaveHappenedOnceExactly();
result.Should().BeOfType<NoContentResult>();
}

[Fact]
public async Task GetAppProviderRolesAsync_ReturnsExpected()
{
// Arrange
var appId = _fixture.Create<Guid>();
var language = _fixture.Create<string>();
var activeAppRoleDetails = _fixture.CreateMany<ActiveAppRoleDetails>(5).ToImmutableArray();

A.CallTo(() => _logic.GetAppProviderRolesAsync(A<Guid>._, A<string>._))
.Returns(activeAppRoleDetails);

// Act
var result = await _controller.GetAppProviderRolesAsync(appId, language);

// Assert
A.CallTo(() => _logic.GetAppProviderRolesAsync(appId, language))
.MustHaveHappenedOnceExactly();
result.Should().HaveSameCount(activeAppRoleDetails)
.And.ContainInOrder(activeAppRoleDetails);
}
}
Loading
Loading