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 } }