// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using GraphQL.DataLoader; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Contents.Queries; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { public sealed class GraphQLExecutionContext : QueryExecutionContext { private static readonly List EmptyAssets = new List(); private static readonly List EmptyContents = new List(); private readonly IDataLoaderContextAccessor dataLoaders; public override Context Context { get; } public GraphQLExecutionContext( IDataLoaderContextAccessor dataLoaders, IAssetQueryService assetQuery, IAssetCache assetCache, IContentQueryService contentQuery, IContentCache contentCache, IServiceProvider serviceProvider, Context context) : base(assetQuery, assetCache, contentQuery, contentCache, serviceProvider) { this.dataLoaders = dataLoaders; Context = context.Clone(b => b .WithoutCleanup() .WithoutContentEnrichment()); } public async ValueTask FindUserAsync(RefToken refToken, CancellationToken ct) { if (refToken.IsClient) { return new ClientUser(refToken); } else { var dataLoader = GetUserLoader(); return await dataLoader.LoadAsync(refToken.Identifier).GetResultAsync(ct); } } public async Task FindAssetAsync(DomainId id, CancellationToken ct) { var dataLoader = GetAssetsLoader(); return await dataLoader.LoadAsync(id).GetResultAsync(ct); } public async Task FindContentAsync(DomainId schemaId, DomainId id, CancellationToken ct) { var dataLoader = GetContentsLoader(); var content = await dataLoader.LoadAsync(id).GetResultAsync(ct); if (content?.SchemaId.Id != schemaId) { content = null; } return content; } public Task> GetReferencedAssetsAsync(JsonValue value, TimeSpan cacheDuration, CancellationToken ct) { var ids = ParseIds(value); return GetAssetsAsync(ids, cacheDuration, ct); } public async Task> GetAssetsAsync(List? ids, TimeSpan cacheDuration, CancellationToken ct) { if (ids == null || ids.Count == 0) { return EmptyAssets; } async Task> LoadAsync(IEnumerable ids) { var result = await GetAssetsLoader().LoadAsync(ids).GetResultAsync(ct); return result?.NotNull().ToList() ?? EmptyAssets; } if (cacheDuration > TimeSpan.Zero) { var assets = await AssetCache.CacheOrQueryAsync(ids, async pendingIds => { return await LoadAsync(pendingIds); }, cacheDuration); return assets; } return await LoadAsync(ids); } public Task> GetReferencedContentsAsync(JsonValue value, TimeSpan cacheDuration, CancellationToken ct) { var ids = ParseIds(value); return GetContentsAsync(ids, cacheDuration, ct); } public async Task> GetContentsAsync(List? ids, TimeSpan cacheDuration, CancellationToken ct) { if (ids == null || ids.Count == 0) { return EmptyContents; } async Task> LoadAsync(IEnumerable ids) { var result = await GetContentsLoader().LoadAsync(ids).GetResultAsync(ct); return result?.NotNull().ToList() ?? EmptyContents; } if (cacheDuration > TimeSpan.Zero) { var contents = await ContentCache.CacheOrQueryAsync(ids, async pendingIds => { return await LoadAsync(pendingIds); }, cacheDuration); return contents.ToList(); } return await LoadAsync(ids); } private IDataLoader GetAssetsLoader() { return dataLoaders.Context!.GetOrAddBatchLoader(nameof(GetAssetsLoader), async (batch, ct) => { var result = await GetReferencedAssetsAsync(new List(batch), ct); return result.ToDictionary(x => x.Id); }); } private IDataLoader GetContentsLoader() { return dataLoaders.Context!.GetOrAddBatchLoader(nameof(GetContentsLoader), async (batch, ct) => { var result = await GetReferencedContentsAsync(new List(batch), ct); return result.ToDictionary(x => x.Id); }); } private IDataLoader GetUserLoader() { return dataLoaders.Context!.GetOrAddBatchLoader(nameof(GetUserLoader), async (batch, ct) => { var result = await Resolve().QueryManyAsync(batch.ToArray(), ct); return result; }); } private static List? ParseIds(JsonValue value) { try { List? result = null; if (value.Value is JsonArray a) { foreach (var item in a) { if (item.Value is string id) { result ??= new List(); result.Add(DomainId.Create(id)); } } } return result; } catch { return null; } } } }