Skip to content

Commit

Permalink
feat(apps): add status filter to GET: apps/provided (#247)
Browse files Browse the repository at this point in the history
Refs: CPLP-3206
Reviewed-by: Phil Schneider <[email protected]>
  • Loading branch information
qxz2mqe authored Sep 7, 2023
1 parent 8c3000a commit deb4f67
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 46 deletions.
14 changes: 12 additions & 2 deletions src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,18 @@ public Task UnsubscribeOwnCompanyAppSubscriptionAsync(Guid subscriptionId, Guid
_offerService.UnsubscribeOwnCompanySubscriptionAsync(subscriptionId, companyId);

/// <inheritdoc/>
public IAsyncEnumerable<AllOfferData> GetCompanyProvidedAppsDataForUserAsync(Guid companyId) =>
_portalRepositories.GetInstance<IOfferRepository>().GetProvidedOffersData(OfferTypeId.APP, companyId);
public Task<Pagination.Response<AllOfferData>> GetCompanyProvidedAppsDataForUserAsync(int page, int size, Guid companyId, OfferSorting? sorting, string? offerName, AppStatusIdFilter? statusId) =>
Pagination.CreateResponseAsync(page, size, 15,
_portalRepositories.GetInstance<IOfferRepository>().GetProvidedOffersData(GetOfferStatusIds(statusId), OfferTypeId.APP, companyId, sorting ?? OfferSorting.DateDesc, offerName));

private static IEnumerable<OfferStatusId> GetOfferStatusIds(AppStatusIdFilter? appStatusIdFilter) =>
appStatusIdFilter switch
{
AppStatusIdFilter.Active => Enumerable.Repeat(OfferStatusId.ACTIVE, 1),
AppStatusIdFilter.Inactive => Enumerable.Repeat(OfferStatusId.INACTIVE, 1),
AppStatusIdFilter.WIP => Enumerable.Repeat(OfferStatusId.CREATED, 1),
_ => Enum.GetValues<OfferStatusId>()
};

/// <inheritdoc />
public Task<OfferAutoSetupResponseData> AutoSetupAppAsync(OfferAutoSetupData data, (Guid UserId, Guid CompanyId) identity) =>
Expand Down
12 changes: 8 additions & 4 deletions src/marketplace/Apps.Service/BusinessLogic/IAppsBusinessLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,16 @@ public interface IAppsBusinessLogic
/// <summary>
/// Retrieve Company Owned App Data
/// </summary>
/// <param name="companyId">Id of the users company to retrieve own app.</param>
/// <returns>Async enumerable of company owned apps data</returns>
IAsyncEnumerable<AllOfferData> GetCompanyProvidedAppsDataForUserAsync(Guid companyId);
/// <param name="page"></param>
/// <param name="size"></param>
/// <param name="companyId"></param>
/// <param name="sorting"></param>
/// <param name="offerName"></param>
/// <param name="statusId"></param>
Task<Pagination.Response<AllOfferData>> GetCompanyProvidedAppsDataForUserAsync(int page, int size, Guid companyId, OfferSorting? sorting, string? offerName, AppStatusIdFilter? statusId);

/// <summary>
/// Auto setup the app.
/// Auto setup the app.AppStatusIdFilter
/// </summary>
/// <param name="data">The offer subscription id and url for the service</param>
/// <param name="identity">Identity of the user</param>
Expand Down
7 changes: 3 additions & 4 deletions src/marketplace/Apps.Service/Controllers/AppsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,13 @@ public async Task<IActionResult> UnsubscribeCompanyAppSubscriptionAsync([FromRou
/// <returns>Collection of app data of apps that are provided by the calling users company</returns>
/// <remarks>Example: GET: /api/apps/provided</remarks>
/// <response code="200">Returns list of apps provided by the user assigned company.</response>

[HttpGet]
[Route("provided")]
[Authorize(Roles = "app_management")]
[Authorize(Policy = PolicyTypes.ValidCompany)]
[ProducesResponseType(typeof(IAsyncEnumerable<AllOfferData>), StatusCodes.Status200OK)]
public IAsyncEnumerable<AllOfferData> GetAppDataAsync() =>
this.WithCompanyId(companyId => _appsBusinessLogic.GetCompanyProvidedAppsDataForUserAsync(companyId));
[ProducesResponseType(typeof(Pagination.Response<AllOfferData>), StatusCodes.Status200OK)]
public Task<Pagination.Response<AllOfferData>> GetAppDataAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] OfferSorting? sorting = null, [FromQuery] string? offerName = null, [FromQuery] AppStatusIdFilter? statusId = null) =>
this.WithCompanyId(companyId => _appsBusinessLogic.GetCompanyProvidedAppsDataForUserAsync(page, size, companyId, sorting, offerName, statusId));

/// <summary>
/// Auto setup the app
Expand Down
47 changes: 47 additions & 0 deletions src/marketplace/Apps.Service/ViewModels/AppStatusIdFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/********************************************************************************
* Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 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.Portal.Backend.Apps.Service.ViewModels;

/// <summary>
/// Filters the OfferStatusId information
/// </summary>
public enum AppStatusIdFilter
{
/// <summary>
/// Filters the Active OfferStatusId information
/// </summary>
Active = 1,

/// <summary>
/// Filters the InActive OfferStatusId information
/// </summary>
Inactive = 2,

/// <summary>
/// Filters the InReview OfferStatusId information
/// </summary>
WIP = 3,

/// <summary>
/// Filters the All OfferStatusId information
/// </summary>
All = 4
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,12 @@ public interface IOfferRepository
/// <summary>
/// Retrieve all app data
/// </summary>
/// <param name="offerTypeId">Offer Type</param>
/// <param name="userCompanyId">Id of the user company</param>
/// <returns>Return Async Enumerable of App Data</returns>
IAsyncEnumerable<AllOfferData> GetProvidedOffersData(OfferTypeId offerTypeId, Guid userCompanyId);
/// <param name="offerStatusIds"></param>
/// <param name="offerTypeId"></param>
/// <param name="userCompanyId"></param>
/// <param name="sorting"></param>
/// <param name="offerName"></param>
Func<int, int, Task<Pagination.Source<AllOfferData>?>> GetProvidedOffersData(IEnumerable<OfferStatusId> offerStatusIds, OfferTypeId offerTypeId, Guid userCompanyId, OfferSorting sorting, string? offerName);

/// <summary>
/// Gets the client roles for a specific app
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,21 +200,34 @@ public void AddAppLanguages(IEnumerable<(Guid appId, string languageShortName)>
public void RemoveAppLanguages(IEnumerable<(Guid appId, string languageShortName)> appLanguageIds) =>
_context.RemoveRange(appLanguageIds.Select(x => new AppLanguage(x.appId, x.languageShortName)));

public IAsyncEnumerable<AllOfferData> GetProvidedOffersData(OfferTypeId offerTypeId, Guid userCompanyId) =>
_context.Offers
.AsNoTracking()
.Where(offer =>
offer.OfferTypeId == offerTypeId &&
offer.ProviderCompanyId == userCompanyId)
.Select(offer => new AllOfferData(
public Func<int, int, Task<Pagination.Source<AllOfferData>?>> GetProvidedOffersData(IEnumerable<OfferStatusId> offerStatusIds, OfferTypeId offerTypeId, Guid userCompanyId, OfferSorting sorting, string? offerName) =>
(skip, take) => Pagination.CreateSourceQueryAsync(
skip,
take,
_context.Offers.AsNoTracking()
.Where(offer =>
offer.OfferTypeId == offerTypeId &&
offer.ProviderCompanyId == userCompanyId &&
offerStatusIds.Contains(offer.OfferStatusId) &&
(offerName == null || EF.Functions.ILike(offer.Name!, $"%{offerName.EscapeForILike()}%")))
.GroupBy(offer => offer.OfferTypeId),
sorting switch
{
OfferSorting.DateAsc => (IEnumerable<Offer> offers) => offers.OrderBy(offer => offer.DateCreated),
OfferSorting.DateDesc => (IEnumerable<Offer> offers) => offers.OrderByDescending(offer => offer.DateCreated),
OfferSorting.NameAsc => (IEnumerable<Offer> offers) => offers.OrderBy(offer => offer.Name),
OfferSorting.NameDesc => (IEnumerable<Offer> offers) => offers.OrderByDescending(offer => offer.Name),
_ => throw new ArgumentOutOfRangeException(nameof(sorting), sorting, null)
},
offer => new AllOfferData(
offer.Id,
offer.Name,
offer.Documents.Where(document => document.DocumentTypeId == DocumentTypeId.APP_LEADIMAGE && document.DocumentStatusId != DocumentStatusId.INACTIVE).Select(document => document.Id).FirstOrDefault(),
offer.Provider,
offer.OfferStatusId,
offer.DateLastChanged
))
.AsAsyncEnumerable();
))
.SingleOrDefaultAsync();

/// <inheritdoc />
[Obsolete("only referenced by code that is marked as obsolte")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using FakeItEasy;
using FluentAssertions;
using Microsoft.Extensions.Options;
using Org.Eclipse.TractusX.Portal.Backend.Apps.Service.ViewModels;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration;
Expand All @@ -35,7 +36,6 @@
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities;
using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared;
using System.Collections.Immutable;
using Xunit;

Expand Down Expand Up @@ -67,9 +67,12 @@ public AppBusinessLogicTests()
_offerRepository = A.Fake<IOfferRepository>();
_offerSubscriptionRepository = A.Fake<IOfferSubscriptionsRepository>();
_notificationRepository = A.Fake<INotificationRepository>();

A.CallTo(() => _portalRepositories.GetInstance<IOfferRepository>()).Returns(_offerRepository);
A.CallTo(() => _portalRepositories.GetInstance<IOfferSubscriptionsRepository>()).Returns(_offerSubscriptionRepository);
A.CallTo(() => _portalRepositories.GetInstance<INotificationRepository>()).Returns(_notificationRepository);

_fixture.Inject(_portalRepositories);
}

[Fact]
Expand Down Expand Up @@ -534,22 +537,32 @@ public async Task GetAppDocumentContentAsync_ReturnsExpectedResult()

#region GetCompanyProvidedAppsDataForUserAsync

[Fact]
public async Task GetCompanyProvidedAppsDataForUserAsync_ReturnsExpectedCount()
[Theory]
[InlineData(AppStatusIdFilter.Active, new[] { OfferStatusId.ACTIVE })]
[InlineData(AppStatusIdFilter.Inactive, new[] { OfferStatusId.INACTIVE })]
[InlineData(AppStatusIdFilter.WIP, new[] { OfferStatusId.CREATED })]
[InlineData(null, new[] { OfferStatusId.CREATED, OfferStatusId.IN_REVIEW, OfferStatusId.ACTIVE, OfferStatusId.INACTIVE })]
public async Task GetCompanyProvidedAppsDataForUserAsync_ReturnsExpectedCount(AppStatusIdFilter? serviceStatusIdFilter, IEnumerable<OfferStatusId> offerStatusIds)
{
//Arrange
var data = new AsyncEnumerableStub<AllOfferData>(_fixture.CreateMany<AllOfferData>(5));
// Arrange
var serviceDetailData = _fixture.CreateMany<AllOfferData>(10).ToImmutableArray();
var paginationResult = (int skip, int take) => Task.FromResult(new Pagination.Source<AllOfferData>(serviceDetailData.Length, serviceDetailData.Skip(skip).Take(take)));
var sorting = _fixture.Create<OfferSorting>();
var name = _fixture.Create<string>();

A.CallTo(() => _offerRepository.GetProvidedOffersData(A<OfferTypeId>._, A<Guid>._))
.Returns(data.AsAsyncEnumerable());
A.CallTo(() => _offerRepository.GetProvidedOffersData(A<IEnumerable<OfferStatusId>>._, A<OfferTypeId>._, A<Guid>._, A<OfferSorting>._, A<string?>._))!
.Returns(paginationResult);

var sut = new AppsBusinessLogic(_portalRepositories, null!, null!, null!, _fixture.Create<IOptions<AppsSettings>>(), _mailingService);
var sut = _fixture.Create<AppsBusinessLogic>();

//Act
var result = await sut.GetCompanyProvidedAppsDataForUserAsync(_identity.CompanyId).ToListAsync().ConfigureAwait(false);
// Act
var result = await sut.GetCompanyProvidedAppsDataForUserAsync(2, 3, _identity.CompanyId, sorting, name, serviceStatusIdFilter).ConfigureAwait(false);

//Assert
result.Should().HaveSameCount(data);
// Assert
A.CallTo(() => _offerRepository.GetProvidedOffersData(A<IEnumerable<OfferStatusId>>
.That.IsSameSequenceAs(offerStatusIds), OfferTypeId.APP, _identity.CompanyId, sorting, name)).MustHaveHappenedOnceExactly();
result.Content.Should().HaveCount(3)
.And.ContainInOrder(serviceDetailData.Skip(6).Take(3));
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,17 @@ public async Task UnsubscribeCompanyAppSubscription_ReturnsNoContent()
public async Task GetAppDataAsync_ReturnsExpectedCount()
{
//Arrange
var data = new AsyncEnumerableStub<AllOfferData>(_fixture.CreateMany<AllOfferData>(5));
A.CallTo(() => _logic.GetCompanyProvidedAppsDataForUserAsync(A<Guid>._))
.Returns(data.AsAsyncEnumerable());
var data = _fixture.CreateMany<AllOfferData>(5);
var paginationResponse = new Pagination.Response<AllOfferData>(new Pagination.Metadata(data.Count(), 1, 0, data.Count()), data);
A.CallTo(() => _logic.GetCompanyProvidedAppsDataForUserAsync(0, 15, _identity.CompanyId, null, null, null))
.Returns(paginationResponse);

//Act
var result = await this._controller.GetAppDataAsync().ToListAsync().ConfigureAwait(false);
var result = await this._controller.GetAppDataAsync().ConfigureAwait(false);

//Assert
A.CallTo(() => _logic.GetCompanyProvidedAppsDataForUserAsync(_identity.CompanyId)).MustHaveHappenedOnceExactly();
result.Should().HaveSameCount(data);
A.CallTo(() => _logic.GetCompanyProvidedAppsDataForUserAsync(0, 15, _identity.CompanyId, null, null, null)).MustHaveHappenedOnceExactly();
result.Content.Should().HaveCount(5);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,18 +601,19 @@ public async Task GetProvidedOffersData_ReturnsExpectedResult(OfferTypeId offerT
var sut = await CreateSut().ConfigureAwait(false);

// Act
var providerAppData = await sut.GetProvidedOffersData(offerTypeId, userCompanyId).ToListAsync().ConfigureAwait(false);
var providerAppData = await sut.GetProvidedOffersData(Enum.GetValues<OfferStatusId>(), offerTypeId, userCompanyId, OfferSorting.DateAsc, null)(0, 10).ConfigureAwait(false);

// Assert
providerAppData.Should().HaveSameCount(offerIds);
if (offerIds.Any())
{
providerAppData.Select(x => x.Id).Should().Contain(offerIds.Select(x => new Guid(x)));
providerAppData.Join(offerIds.Select(x => new Guid(x)).Zip(leadPictureIds.Select(x => new Guid(x))), data => data.Id, zip => zip.First, (data, zip) => (data, zip)).Should().AllSatisfy(x => x.data.LeadPictureId.Should().Be(x.zip.Second));
providerAppData.Should().NotBeNull();
providerAppData!.Data.Should().HaveSameCount(offerIds);
providerAppData.Data.Select(x => x.Id).Should().Contain(offerIds.Select(x => new Guid(x)));
providerAppData.Data.Join(offerIds.Select(x => new Guid(x)).Zip(leadPictureIds.Select(x => new Guid(x))), data => data.Id, zip => zip.First, (data, zip) => (data, zip)).Should().AllSatisfy(x => x.data.LeadPictureId.Should().Be(x.zip.Second));
}
else
{
providerAppData.Should().BeEmpty();
providerAppData.Should().BeNull();
}
}

Expand Down

0 comments on commit deb4f67

Please sign in to comment.