using System;
using System.Linq;
using xModels.Dtos;
using System.Threading;
using xCommons.Extensions;
using xIdentityModels.Dtos;
using xExceptions.Constants;
using System.Threading.Tasks;
using xIdentityModels.Models;
using System.Linq.Expressions;
using xDataService.Extensions;
using System.Collections.Generic;
using xIdentityModels.Extensions;
using Microsoft.EntityFrameworkCore;
using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace xIds.Providers
{
public partial class XApplicationProvider
{
//
#region ApiKey ...
///
/// Add Specified Item ...
///
///
///
///
///
public async Task AddApiKeyAsync(
XApiKey item,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (!item.IsValidForAdd())
{
XException.InvalidArgs.Throw();
}
//
// Permission ...
if (userInfo.IsNullOrDefault())
{
XException.NotAllowed.Throw();
}
//
// Check Application Exists ...
var application = await GetApplicationAsync(
userInfo: userInfo,
includeBuilder: null,
id: item.ApplicationId,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (application.IsNullOrDefault())
{
XException.NotFound.Throw();
}
//
// Validate Owner ...
if (!IsOwned(application, userInfo))
{
XException.NotAllowed.Throw();
}
//
// Check COunt of API Keys ...
var appApiKeysCount = await dbContext.ApiKeys.CountAsync(
cancellationToken: cancellationToken,
predicate: x => x.ApplicationId == item.ApplicationId
);
if (appApiKeysCount >= apiKeyConfiguration.MaxKeysPerApplication)
{
XException.NotAllowed.Throw();
}
//
item.CreatedOn = DateTime.UtcNow;
//
var entity = item.MapConvert();
if (entity.Id.IsValidIntId())
{
entity.Id = -1;
}
//
var entry = await dbContext.ApiKeys.AddAsync(
entity: entity,
cancellationToken: cancellationToken
);
var changes = await dbContext.SaveChangesAsync(cancellationToken);
//
if (
changes <= 0 ||
entry.IsNull() ||
entry.Entity.IsNullOrDefault()
)
{
XException.ActionFailed.Throw();
}
//
var result = await EnrichAsync(
source: entry.Entity,
cancellationToken: cancellationToken
);
return result;
}
///
/// Update Specified Item ...
///
///
///
///
///
///
public async Task UpdateApiKeyAsync(
Guid id,
XApiKeyDto item,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (
id != item.Id ||
!id.IsDefaultGuid() ||
!item.IsValidForAdd() ||
item.IsNullOrDefault() ||
userInfo.IsNullOrDefault()
)
{
XException.InvalidArgs.Throw();
}
//
// Retrieve Exists Entity ...
var exists = await dbContext.ApiKeys
.FirstOrDefaultAsync(x => x.Id == id && !x.Deleted);
if (exists.IsNullOrDefault())
{
XException.NotFound.Throw();
}
//
// Permission Checking ...
var application = await GetApplicationAsync(
userInfo: userInfo,
includeBuilder: null,
id: item.ApplicationId,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (application.IsNullOrDefault())
{
XException.NotFound.Throw();
}
if (!IsOwned(application, userInfo))
{
XException.NotAllowed.Throw();
}
//
// Update Data on Exists Entity ...
exists = exists.UpdateData(
updateWith: item,
propertyWhiteList: [
nameof(XApiKey.KeyHash),
nameof(XApiKey.KeyPrefix),
nameof(XApiKey.ExpiresAt),
nameof(XApiKey.CreatedOn),
nameof(XApiKey.RevokedAt),
nameof(XApiKey.RevokedBy),
nameof(XApiKey.LastUsedAt),
nameof(XApiKey.AllowedIPs),
nameof(XApiKey.ApplicationId),
nameof(XApiKey.AllowedScopes),
nameof(XApiKey.RateLimitPerMinute),
]
);
//
var entry = dbContext.ApiKeys.Attach(exists);
entry.State = EntityState.Modified;
//
var changes = await dbContext.SaveChangesAsync(cancellationToken);
if (
changes <= 0 ||
entry.IsNull() ||
entry.Entity.IsNullOrDefault()
)
{
XException.ActionFailed.Throw();
}
//
var result = await EnrichAsync(
source: entry.Entity,
cancellationToken: cancellationToken
);
return result;
}
///
/// Remove Specified Item ...
///
///
///
///
///
///
public async Task RemoveApiKeyAsync(
Guid id,
bool softDelete = true,
XUserClaimsInfoDto userInfo = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (
id.IsDefaultGuid() ||
userInfo.IsNullOrDefault()
)
{
XException.InvalidArgs.Throw();
}
//
// Retrieve Exists Entity ...
var exists = await dbContext.ApiKeys
.FirstOrDefaultAsync(x => x.Id == id && !x.Deleted);
if (exists.IsNullOrDefault())
{
XException.NotFound.Throw();
}
//
// Permission Checking ...
var application = await GetApplicationAsync(
userInfo: userInfo,
includeBuilder: null,
id: exists.ApplicationId,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (application.IsNullOrDefault())
{
XException.NotFound.Throw();
}
if (!IsOwned(application, userInfo))
{
XException.NotAllowed.Throw();
}
//
EntityEntry entry = null;
if (softDelete)
{
//
exists.Deleted = true;
entry = dbContext.ApiKeys.Update(exists);
}
else
{
entry = dbContext.ApiKeys.Remove(exists);
}
//
var changes = await dbContext.SaveChangesAsync(cancellationToken);
if (
changes <= 0 ||
entry.IsNull() ||
entry.Entity.IsNull()
)
{
XException.ActionFailed.Throw();
}
//
var result = await EnrichAsync(
source: entry.Entity,
cancellationToken: cancellationToken
);
return result;
}
///
/// Retrieve an Item by Providing it's ID ...
///
///
///
///
///
///
///
public async Task GetApiKeyAsync(
Guid id,
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Func, IIncludableQueryable> includeBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (id.IsDefaultGuid() ||
userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
// Prepare Condition ...
Expression> predicate = x =>
!ignoreSoftDeleteds || !x.Deleted;
//
// Retrieve Queryable ...
var queryable = GetApiKeysQeryable(
orderBuilder: null,
predicate: predicate,
includeBuilder: includeBuilder
);
//
var entity = await queryable.FirstOrDefaultAsync(
predicate: x => x.Id == id,
cancellationToken: cancellationToken
);
if (entity.IsNullOrDefault())
{
XException.NotFound.Throw();
}
//
// Permission Checking ...
var application = await GetApplicationAsync(
userInfo: userInfo,
includeBuilder: null,
id: entity.ApplicationId,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (application.IsNullOrDefault())
{
XException.NotFound.Throw();
}
if (!IsOwned(application, userInfo))
{
XException.NotAllowed.Throw();
}
//
var result = await EnrichAsync(
source: entity,
cancellationToken: cancellationToken
);
return result;
}
///
/// Retrieve all Items ...
///
///
///
///
///
///
///
public async Task> GetAllApiKeyAsync(
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Func, IOrderedQueryable> orderBuilder = null,
Func, IIncludableQueryable> includeBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
var applications = await GetAllApplicationAsync(
userInfo: userInfo,
orderBuilder: null,
includeBuilder: null,
cancellationToken: cancellationToken,
ignoreSoftDeleteds: ignoreSoftDeleteds
);
applications = applications.Where(a => IsOwned(a, userInfo));
var appIds = applications
.Select(a => a.Id)
.ToList();
//
Expression> predicate = x =>
appIds.Contains(x.ApplicationId) &&
!ignoreSoftDeleteds || !x.Deleted;
var queryable = GetApiKeysQeryable(
predicate: predicate,
orderBuilder: orderBuilder,
includeBuilder: includeBuilder
);
//
var items = await queryable.ToListAsync(cancellationToken);
var result = await EnrichRangeAsync(
sources: items,
cancellationToken: cancellationToken
);
return result;
}
///
/// Retrieve all Items as Async Enumerable ...
///
///
///
///
///
///
///
public async IAsyncEnumerable GetAllApiKeyAsAsyncEnumerable(
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Func, IOrderedQueryable> orderBuilder = null,
Func, IIncludableQueryable> includeBuilder = null,
[EnumeratorCancellation]
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
var applications = await GetAllApplicationAsync(
userInfo: userInfo,
orderBuilder: null,
includeBuilder: null,
cancellationToken: cancellationToken,
ignoreSoftDeleteds: ignoreSoftDeleteds
);
applications = applications.Where(a => IsOwned(a, userInfo));
var appIds = applications
.Select(a => a.Id)
.ToList();
//
Expression> predicate = x =>
appIds.Contains(x.ApplicationId) &&
!ignoreSoftDeleteds || !x.Deleted;
var queryable = GetApiKeysQeryable(
predicate: predicate,
orderBuilder: orderBuilder,
includeBuilder: includeBuilder
);
//
var enumerator = queryable.AsAsyncEnumerable();
await foreach (var item in enumerator)
{
//
// Handle Cancellation Token ...
if (cancellationToken.IsCancellationRequested)
{
yield break;
}
//
var dto = await EnrichAsync(
source: item,
cancellationToken: cancellationToken
);
yield return dto;
}
}
///
/// Find an Item By Providing a Conditional Expression ...
///
///
///
///
///
///
///
public async Task FindOneApiKeyAsync(
Expression> predicate,
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Func, IIncludableQueryable> includeBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
Expression> _predicate = x => !ignoreSoftDeleteds || !x.Deleted;
var queryable = GetApiKeysQeryable(
orderBuilder: null,
predicate: _predicate,
includeBuilder: includeBuilder
);
//
var entity = (await queryable
.ToListAsync(cancellationToken))
.FirstOrDefault(x => predicate.Compile()(x));
//
// Permission Checking ...
var application = await GetApplicationAsync(
userInfo: userInfo,
includeBuilder: null,
id: entity.ApplicationId,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (application.IsNullOrDefault())
{
XException.NotFound.Throw();
}
if (!IsOwned(application, userInfo))
{
XException.NotAllowed.Throw();
}
//
var result = await EnrichAsync(
source: entity,
cancellationToken: cancellationToken
);
return result;
}
///
/// Find a Collection of Items By Providing a Conditional Expression ...
///
///
///
///
///
///
///
///
public async Task> FindManyApiKeyAsync(
Expression> predicate,
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Func, IOrderedQueryable> orderBuilder = null,
Func, IIncludableQueryable> includeBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
Expression> _predicate = x => !ignoreSoftDeleteds || !x.Deleted;
var queryable = GetApiKeysQeryable(
orderBuilder: null,
predicate: _predicate,
includeBuilder: includeBuilder
);
//
var entities = (await queryable
.ToListAsync(cancellationToken))
.Where(x => predicate.Compile()(x))
.ToList();
//
var appIds = entities
.Select(k => k.ApplicationId)
.ToList();
//
foreach (var appId in appIds)
{
//
// Permission Checking ...
var application = await GetApplicationAsync(
id: appId,
userInfo: userInfo,
includeBuilder: null,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (
application.IsNullOrDefault() ||
!IsOwned(application, userInfo))
{
entities = [.. entities.Where(e => e.ApplicationId != appId)];
}
}
//
var result = await EnrichRangeAsync(
sources: entities,
cancellationToken: cancellationToken
);
return result;
}
///
/// Retrieve Items based on XQuery Pagination Structure ...
///
///
///
///
///
///
///
///
///
public async Task> QueryApiKeyAsync(
XQuery query,
bool ignoreSoftDeleteds = true,
XUserClaimsInfoDto userInfo = null,
Expression> predicate = null,
Func, IOrderedQueryable> orderBuilder = null,
Func, IIncludableQueryable> includeBuilder = null,
CancellationToken cancellationToken = default
)
{
//
// Validate ...
if (userInfo.IsNullOrDefault())
{
XException.InvalidArgs.Throw();
}
//
// Normalize Query ...
query = NormalizeQuery(query, configuration);
//
Expression> _predicate = x => !ignoreSoftDeleteds || !x.Deleted;
var queryable = GetApiKeysQeryable(
predicate: _predicate,
orderBuilder: orderBuilder,
includeBuilder: includeBuilder
);
//
var items = (await queryable
.ToListAsync(cancellationToken))
.Where(x => predicate.Compile()(x));
//
var appIds = items
.Select(k => k.ApplicationId)
.ToList();
//
foreach (var appId in appIds)
{
//
// Permission Checking ...
var application = await GetApplicationAsync(
id: appId,
userInfo: userInfo,
includeBuilder: null,
ignoreSoftDeleteds: true,
cancellationToken: cancellationToken
);
if (
application.IsNullOrDefault() ||
!IsOwned(application, userInfo))
{
items = [.. items.Where(e => e.ApplicationId != appId)];
}
}
//
var totalItemsCount = items.Count();
//
// Apply Filter ...
if (!query.Filter.IsNullOrEmpty())
{
//
items = items
.AsEnumerable()
.ApplyFilter(query.Filter);
}
int filteredItemsCount = items.Count();
//
// Count Pages ...
var totalPagesCount = query.CountPages(totalItemsCount);
var filteredPagesCount = query.CountPages(filteredItemsCount);
//
// Apply Paging and Sorting ...
if (totalItemsCount > 0 &&
filteredItemsCount > 0
)
{
//
// Apply Sorting ...
items = items
.ToList()
.ApplySorting(
query.SortBy,
query.IsAscending
);
//
// Apply Paging ...
items = items
.ToList()
.ApplyPaging(
query.Page,
query.PageSize
);
}
//
var resultItems = await EnrichRangeAsync(
sources: items,
cancellationToken: cancellationToken
);
//
// Prepare Result ...
var result = new XQueryResult
{
Page = query.Page,
Items = [.. resultItems],
PageSize = query.PageSize,
TotalPages = totalPagesCount,
TotalItems = totalItemsCount,
TotalFilteredPages = filteredPagesCount,
TotalFilteredItems = filteredItemsCount
};
//
return result;
}
#endregion
}
}