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
20 changes: 20 additions & 0 deletions src/Core/Services/IApplicationCacheService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,29 @@ public interface IApplicationCacheService
Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync();
#nullable enable
Task<OrganizationAbility?> GetOrganizationAbilityAsync(Guid orgId);
/// <summary>
/// Gets the cached <see cref="ProviderAbility"/> for the specified provider.
/// </summary>
/// <param name="providerId">The ID of the provider.</param>
/// <returns>The <see cref="ProviderAbility"/> if found; otherwise, <c>null</c>.</returns>
Task<ProviderAbility?> GetProviderAbilityAsync(Guid providerId);
#nullable disable
[Obsolete("We are transitioning to a new cache pattern. Please consult the Admin Console team before using.", false)]
Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync();
/// <summary>
/// Gets cached <see cref="ProviderAbility"/> entries for the specified providers.
/// Provider IDs not found in the cache are silently excluded from the result.
/// </summary>
/// <param name="providerIds">The IDs of the providers to look up.</param>
/// <returns>A dictionary mapping each found provider ID to its <see cref="ProviderAbility"/>.</returns>
Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync(IEnumerable<Guid> providerIds);
/// <summary>
/// Gets cached <see cref="OrganizationAbility"/> entries for the specified organizations.
/// Organization IDs not found in the cache are silently excluded from the result.
/// </summary>
/// <param name="orgIds">The IDs of the organizations to look up.</param>
/// <returns>A dictionary mapping each found organization ID to its <see cref="OrganizationAbility"/>.</returns>
Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync(IEnumerable<Guid> orgIds);
Task UpsertOrganizationAbilityAsync(Organization organization);
Task UpsertProviderAbilityAsync(Provider provider);
Task DeleteOrganizationAbilityAsync(Guid organizationId);
Expand Down
22 changes: 22 additions & 0 deletions src/Core/Services/Implementations/FeatureRoutedCacheService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ public Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsyn
public Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync() =>
inMemoryApplicationCacheService.GetProviderAbilitiesAsync();

public async Task<ProviderAbility?> GetProviderAbilityAsync(Guid providerId)
{
(await GetProviderAbilitiesAsync([providerId])).TryGetValue(providerId, out var providerAbility);
return providerAbility;
}

public async Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync(IEnumerable<Guid> providerIds)
{
var allProviderAbilities = await inMemoryApplicationCacheService.GetProviderAbilitiesAsync();
return providerIds
.Where(allProviderAbilities.ContainsKey)
.ToDictionary(id => id, id => allProviderAbilities[id]);
}

public async Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync(IEnumerable<Guid> orgIds)
{
var allOrganizationAbilities = await inMemoryApplicationCacheService.GetOrganizationAbilitiesAsync();
return orgIds
.Where(allOrganizationAbilities.ContainsKey)
.ToDictionary(id => id, id => allOrganizationAbilities[id]);
}

public Task UpsertOrganizationAbilityAsync(Organization organization) =>
inMemoryApplicationCacheService.UpsertOrganizationAbilityAsync(organization);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ public virtual async Task<IDictionary<Guid, ProviderAbility>> GetProviderAbiliti
return _providerAbilities;
}

#nullable enable
public async Task<ProviderAbility?> GetProviderAbilityAsync(Guid providerId)
{
(await GetProviderAbilitiesAsync()).TryGetValue(providerId, out var providerAbility);
return providerAbility;
}
#nullable disable

public async Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync(IEnumerable<Guid> providerIds)
{
var allProviderAbilities = await GetProviderAbilitiesAsync();
return providerIds
.Where(allProviderAbilities.ContainsKey)
.ToDictionary(id => id, id => allProviderAbilities[id]);
}

public async Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync(IEnumerable<Guid> orgIds)
{
var allOrganizationAbilities = await GetOrganizationAbilitiesAsync();
return orgIds
.Where(allOrganizationAbilities.ContainsKey)
.ToDictionary(id => id, id => allOrganizationAbilities[id]);
}

public virtual async Task UpsertProviderAbilityAsync(Provider provider)
{
await InitProviderAbilitiesAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,123 @@ await sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetProviderAbilitiesAsync();
}

[Theory, BitAutoData]
public async Task GetProviderAbilityAsync_WhenProviderExists_ReturnsAbility(
SutProvider<FeatureRoutedCacheService> sutProvider,
ProviderAbility providerAbility)
{
// Arrange
var allAbilities = new Dictionary<Guid, ProviderAbility> { [providerAbility.Id] = providerAbility };
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetProviderAbilitiesAsync()
.Returns(allAbilities);

// Act
var result = await sutProvider.Sut.GetProviderAbilityAsync(providerAbility.Id);

// Assert
Assert.Equal(providerAbility, result);
}

[Theory, BitAutoData]
public async Task GetProviderAbilityAsync_WhenProviderDoesNotExist_ReturnsNull(
SutProvider<FeatureRoutedCacheService> sutProvider,
Guid providerId)
{
// Arrange
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetProviderAbilitiesAsync()
.Returns(new Dictionary<Guid, ProviderAbility>());

// Act
var result = await sutProvider.Sut.GetProviderAbilityAsync(providerId);

// Assert
Assert.Null(result);
}

[Theory, BitAutoData]
public async Task GetProviderAbilitiesAsync_ReturnsOnlyMatchingAbilities(
SutProvider<FeatureRoutedCacheService> sutProvider,
ProviderAbility matchedAbility,
ProviderAbility unmatchedAbility)
{
// Arrange
var allAbilities = new Dictionary<Guid, ProviderAbility>
{
[matchedAbility.Id] = matchedAbility,
[unmatchedAbility.Id] = unmatchedAbility
};
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetProviderAbilitiesAsync()
.Returns(allAbilities);

// Act
var result = await sutProvider.Sut.GetProviderAbilitiesAsync([matchedAbility.Id]);

// Assert
Assert.Single(result);
Assert.Equal(matchedAbility, result[matchedAbility.Id]);
}

[Theory, BitAutoData]
public async Task GetProviderAbilitiesAsync_WhenNoIdsMatched_ReturnsEmptyDictionary(
SutProvider<FeatureRoutedCacheService> sutProvider,
Guid missingProviderId)
{
// Arrange
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetProviderAbilitiesAsync()
.Returns(new Dictionary<Guid, ProviderAbility>());

// Act
var result = await sutProvider.Sut.GetProviderAbilitiesAsync([missingProviderId]);

// Assert
Assert.Empty(result);
}

[Theory, BitAutoData]
public async Task GetOrganizationAbilitiesAsync_ReturnsOnlyMatchingAbilities(
SutProvider<FeatureRoutedCacheService> sutProvider,
OrganizationAbility matchedAbility,
OrganizationAbility unmatchedAbility)
{
// Arrange
var allAbilities = new Dictionary<Guid, OrganizationAbility>
{
[matchedAbility.Id] = matchedAbility,
[unmatchedAbility.Id] = unmatchedAbility
};
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetOrganizationAbilitiesAsync()
.Returns(allAbilities);

// Act
var result = await sutProvider.Sut.GetOrganizationAbilitiesAsync([matchedAbility.Id]);

// Assert
Assert.Single(result);
Assert.Equal(matchedAbility, result[matchedAbility.Id]);
}

[Theory, BitAutoData]
public async Task GetOrganizationAbilitiesAsync_WhenNoIdsMatched_ReturnsEmptyDictionary(
SutProvider<FeatureRoutedCacheService> sutProvider,
Guid missingOrgId)
{
// Arrange
sutProvider.GetDependency<IVCurrentInMemoryApplicationCacheService>()
.GetOrganizationAbilitiesAsync()
.Returns(new Dictionary<Guid, OrganizationAbility>());

// Act
var result = await sutProvider.Sut.GetOrganizationAbilitiesAsync([missingOrgId]);

// Assert
Assert.Empty(result);
}

[Theory, BitAutoData]
public async Task UpsertOrganizationAbilityAsync_CallsInMemoryService(
SutProvider<FeatureRoutedCacheService> sutProvider,
Expand Down
Loading