diff --git a/OrchardCore.sln b/OrchardCore.sln index b1ba74e51d1..95a51c8d18b 100644 --- a/OrchardCore.sln +++ b/OrchardCore.sln @@ -523,6 +523,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Email.Smtp", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Rules.Core", "src\OrchardCore\OrchardCore.Rules.Core\OrchardCore.Rules.Core.csproj", "{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.Queries.Core", "src\OrchardCore\OrchardCore.Queries.Core\OrchardCore.Queries.Core.csproj", "{61B358F2-702C-40AA-9DF7-7121248FE6DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1383,6 +1385,10 @@ Global {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7}.Release|Any CPU.Build.0 = Release|Any CPU + {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61B358F2-702C-40AA-9DF7-7121248FE6DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1622,6 +1628,7 @@ Global {C35AB37B-5A09-4896-BEEE-B126B7E7018A} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} {E8A1097D-A65A-4B17-A3A2-F50D79552732} = {A066395F-6F73-45DC-B5A6-B4E306110DCE} {4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} + {61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs index bb5f1dbaa2d..f6616cca816 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using OrchardCore.Admin; @@ -16,25 +17,26 @@ using OrchardCore.Navigation; using OrchardCore.Queries.ViewModels; using OrchardCore.Routing; +using YesSql.Services; namespace OrchardCore.Queries.Controllers { [Admin("Queries/{action}/{id?}", "Queries{action}")] - public class AdminController : Controller + public sealed class AdminController : Controller { private const string _optionsSearch = "Options.Search"; private readonly IAuthorizationService _authorizationService; private readonly PagerOptions _pagerOptions; private readonly INotifier _notifier; + private readonly IServiceProvider _serviceProvider; private readonly IQueryManager _queryManager; - private readonly IEnumerable _querySources; private readonly IDisplayManager _displayManager; private readonly IUpdateModelAccessor _updateModelAccessor; private readonly IShapeFactory _shapeFactory; - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; + internal readonly IStringLocalizer S; + internal readonly IHtmlLocalizer H; public AdminController( IDisplayManager displayManager, @@ -45,14 +47,14 @@ public AdminController( IHtmlLocalizer htmlLocalizer, INotifier notifier, IQueryManager queryManager, - IEnumerable querySources, + IServiceProvider serviceProvider, IUpdateModelAccessor updateModelAccessor) { _displayManager = displayManager; _authorizationService = authorizationService; _pagerOptions = pagerOptions.Value; _queryManager = queryManager; - _querySources = querySources; + _serviceProvider = serviceProvider; _updateModelAccessor = updateModelAccessor; _shapeFactory = shapeFactory; _notifier = notifier; @@ -69,19 +71,6 @@ public async Task Index(ContentOptions options, PagerParameters p var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var queries = await _queryManager.ListQueriesAsync(); - queries = queries.OrderBy(x => x.Name); - - if (!string.IsNullOrWhiteSpace(options.Search)) - { - queries = queries.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); - } - - var results = queries - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); - // Maintain previous route data when generating page links. var routeData = new RouteData(); @@ -90,15 +79,20 @@ public async Task Index(ContentOptions options, PagerParameters p routeData.Values.TryAdd(_optionsSearch, options.Search); } + var result = await _queryManager.PageQueriesAsync(pager.Page, pager.PageSize, new QueryContext() + { + Name = options.Search, + }); + var model = new QueriesIndexViewModel { Queries = [], Options = options, - Pager = await _shapeFactory.PagerAsync(pager, queries.Count(), routeData), - QuerySourceNames = _querySources.Select(x => x.Name).ToList() + Pager = await _shapeFactory.PagerAsync(pager, result.Count, routeData), + QuerySourceNames = _serviceProvider.GetServices().Select(x => x.Name).ToArray(), }; - foreach (var query in results) + foreach (var query in result.Records) { model.Queries.Add(new QueryEntry { @@ -115,7 +109,8 @@ public async Task Index(ContentOptions options, PagerParameters p return View(model); } - [HttpPost, ActionName(nameof(Index))] + [HttpPost] + [ActionName(nameof(Index))] [FormValueRequired("submit.Filter")] public ActionResult IndexFilterPOST(QueriesIndexViewModel model) => RedirectToAction(nameof(Index), new RouteValueDictionary @@ -130,7 +125,7 @@ public async Task Create(string id) return Forbid(); } - var query = _querySources.FirstOrDefault(x => x.Name == id)?.Create(); + var query = await _queryManager.NewAsync(id); if (query == null) { @@ -139,14 +134,15 @@ public async Task Create(string id) var model = new QueriesCreateViewModel { - Editor = await _displayManager.BuildEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""), + Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, true), SourceName = id }; return View(model); } - [HttpPost, ActionName(nameof(Create))] + [HttpPost] + [ActionName(nameof(Create))] public async Task CreatePost(QueriesCreateViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) @@ -154,24 +150,25 @@ public async Task CreatePost(QueriesCreateViewModel model) return Forbid(); } - var query = _querySources.FirstOrDefault(x => x.Name == model.SourceName)?.Create(); + var query = await _queryManager.NewAsync(model.SourceName); if (query == null) { return NotFound(); } - var editor = await _displayManager.UpdateEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); + var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, true); if (ModelState.IsValid) { - await _queryManager.SaveQueryAsync(query.Name, query); + await _queryManager.SaveAsync(query); await _notifier.SuccessAsync(H["Query created successfully."]); + return RedirectToAction(nameof(Index)); } - // If we got this far, something failed, redisplay form + // If we got this far, something failed, redisplay form. model.Editor = editor; return View(model); @@ -193,16 +190,15 @@ public async Task Edit(string id) var model = new QueriesEditViewModel { - SourceName = query.Source, Name = query.Name, - Schema = query.Schema, - Editor = await _displayManager.BuildEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") + Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, false) }; return View(model); } - [HttpPost, ActionName(nameof(Edit))] + [HttpPost] + [ActionName(nameof(Edit))] public async Task EditPost(QueriesEditViewModel model) { if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) @@ -210,26 +206,31 @@ public async Task EditPost(QueriesEditViewModel model) return Forbid(); } - var query = await _queryManager.LoadQueryAsync(model.Name); + if (string.IsNullOrEmpty(model.Name)) + { + return BadRequest(); + } + + var query = await _queryManager.GetQueryAsync(model.Name); if (query == null) { return NotFound(); } - var editor = await _displayManager.UpdateEditorAsync(query, updater: _updateModelAccessor.ModelUpdater, isNew: false, string.Empty, string.Empty); + var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, false); if (ModelState.IsValid) { - await _queryManager.SaveQueryAsync(model.Name, query); - + await _queryManager.UpdateAsync(query); await _notifier.SuccessAsync(H["Query updated successfully."]); + return RedirectToAction(nameof(Index)); } model.Editor = editor; - // If we got this far, something failed, redisplay form + // If we got this far, something failed, redisplay form. return View(model); } @@ -241,21 +242,18 @@ public async Task Delete(string id) return Forbid(); } - var query = await _queryManager.LoadQueryAsync(id); - - if (query == null) + if (!await _queryManager.DeleteQueryAsync(id)) { return NotFound(); } - await _queryManager.DeleteQueryAsync(id); - await _notifier.SuccessAsync(H["Query deleted successfully."]); return RedirectToAction(nameof(Index)); } - [HttpPost, ActionName(nameof(Index))] + [HttpPost] + [ActionName(nameof(Index))] [FormValueRequired("submit.BulkAction")] public async Task IndexPost(ContentOptions options, IEnumerable itemIds) { @@ -266,21 +264,16 @@ public async Task IndexPost(ContentOptions options, IEnumerable 0) { - var queriesList = await _queryManager.ListQueriesAsync(); - var checkedContentItems = queriesList.Where(x => itemIds.Contains(x.Name)); switch (options.BulkAction) { case ContentsBulkAction.None: break; case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _queryManager.DeleteQueryAsync(item.Name); - } + await _queryManager.DeleteQueryAsync(itemIds.ToArray()); await _notifier.SuccessAsync(H["Queries successfully removed."]); break; default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); + return BadRequest(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs index 14b8b90aed7..88c7e62938f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs @@ -11,8 +11,10 @@ namespace OrchardCore.Queries.Controllers { [Route("api/queries")] [ApiController] - [Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] - public class QueryApiController : ControllerBase + [Authorize(AuthenticationSchemes = "Api")] + [IgnoreAntiforgeryToken] + [AllowAnonymous] + public sealed class QueryApiController : ControllerBase { private readonly IAuthorizationService _authorizationService; private readonly IQueryManager _queryManager; @@ -57,6 +59,7 @@ public async Task Query( : []; var result = await _queryManager.ExecuteQueryAsync(query, queryParameters); + return new ObjectResult(result); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs index 7add2e2caaa..1f2168b6e56 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs @@ -34,7 +34,7 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan return; } - if (!query.ResultsOfType()) + if (!query.CanReturnContentItems || !query.ReturnContentItems) { return; } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs index 43b71b41959..44f84fe5d4d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs @@ -2,7 +2,6 @@ using System.Text.Json; using System.Threading.Tasks; using Microsoft.Extensions.Localization; -using OrchardCore.ContentManagement; using OrchardCore.Deployment; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.ModelBinding; @@ -15,6 +14,7 @@ namespace OrchardCore.Queries.Deployment public class QueryBasedContentDeploymentStepDriver : DisplayDriver { private readonly IQueryManager _queryManager; + protected readonly IStringLocalizer S; public QueryBasedContentDeploymentStepDriver( @@ -36,11 +36,12 @@ public override IDisplayResult Display(QueryBasedContentDeploymentStep step) public override IDisplayResult Edit(QueryBasedContentDeploymentStep step) { - return Initialize("QueryBasedContentDeploymentStep_Fields_Edit", model => + return Initialize("QueryBasedContentDeploymentStep_Fields_Edit", async model => { model.QueryName = step.QueryName; model.QueryParameters = step.QueryParameters; model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; + model.Queries = await _queryManager.ListQueriesAsync(true); }).Location("Content"); } @@ -48,9 +49,9 @@ public override async Task UpdateAsync(QueryBasedContentDeployme { var queryBasedContentViewModel = new QueryBasedContentDeploymentStepViewModel(); await updater.TryUpdateModelAsync(queryBasedContentViewModel, Prefix, viewModel => viewModel.QueryName, viewModel => viewModel.QueryParameters, viewModel => viewModel.ExportAsSetupRecipe); - var query = await _queryManager.LoadQueryAsync(queryBasedContentViewModel.QueryName); + var query = await _queryManager.GetQueryAsync(queryBasedContentViewModel.QueryName); - if (!query.ResultsOfType()) + if (!query.CanReturnContentItems || !query.ReturnContentItems) { updater.ModelState.AddModelError(Prefix, nameof(step.QueryName), S["Your Query is not returning content items."]); } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs index 1253b8f681c..a5da46bd7c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs @@ -3,17 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; +using OrchardCore.Mvc.ModelBinding; using OrchardCore.Mvc.Utilities; using OrchardCore.Queries.ViewModels; namespace OrchardCore.Queries.Drivers { - public class QueryDisplayDriver : DisplayDriver + public sealed class QueryDisplayDriver : DisplayDriver { private readonly IQueryManager _queryManager; - protected readonly IStringLocalizer S; - public QueryDisplayDriver(IQueryManager queryManager, IStringLocalizer stringLocalizer) + internal readonly IStringLocalizer S; + + public QueryDisplayDriver( + IQueryManager queryManager, + IStringLocalizer stringLocalizer) { _queryManager = queryManager; S = stringLocalizer; @@ -65,24 +69,25 @@ public override async Task UpdateAsync(Query model, IUpdateModel if (string.IsNullOrEmpty(model.Name)) { - updater.ModelState.AddModelError(nameof(model.Name), S["Name is required"]); + updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name is required"]); } + if (!string.IsNullOrEmpty(model.Schema) && !model.Schema.IsJson()) { - updater.ModelState.AddModelError(nameof(model.Schema), S["Invalid schema JSON supplied."]); + updater.ModelState.AddModelError(Prefix, nameof(model.Schema), S["Invalid schema JSON supplied."]); } var safeName = model.Name.ToSafeName(); if (string.IsNullOrEmpty(safeName) || model.Name != safeName) { - updater.ModelState.AddModelError(nameof(model.Name), S["Name contains illegal characters"]); + updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name contains illegal characters"]); } else { - var existing = await _queryManager.LoadQueryAsync(safeName); + var existing = await _queryManager.GetQueryAsync(safeName); if (existing != null && existing != model) { - updater.ModelState.AddModelError(nameof(model.Name), S["A query with the same name already exists"]); + updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["A query with the same name already exists"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs index 7d3f586abe6..ce52bdf98d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs @@ -1,18 +1,20 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using Fluid; using Fluid.Values; +using Microsoft.Extensions.DependencyInjection; using OrchardCore.Liquid; namespace OrchardCore.Queries.Liquid { public class QueryFilter : ILiquidFilter { - private readonly IQueryManager _queryManager; + private readonly IServiceProvider _serviceProvider; - public QueryFilter(IQueryManager queryManager) + public QueryFilter(IServiceProvider serviceProvider) { - _queryManager = queryManager; + _serviceProvider = serviceProvider; } public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) @@ -31,7 +33,9 @@ public async ValueTask ProcessAsync(FluidValue input, FilterArgument parameters.Add(name, arguments[name].ToObjectValue()); } - var result = await _queryManager.ExecuteQueryAsync(query, parameters); + var queryManager = _serviceProvider.GetRequiredService(); + + var result = await queryManager.ExecuteQueryAsync(query, parameters); return FluidValue.Create(result.Items, ctx.Options); } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Manifest.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Manifest.cs index bb8d5dd827a..4cb356c3622 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Manifest.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Manifest.cs @@ -7,11 +7,26 @@ Version = ManifestConstants.OrchardCoreVersion )] +[assembly: Feature( + Id = "OrchardCore.Queries.Core", + Name = "Queries Core Services", + Description = "Provides querying capability services.", + Dependencies = + [ + "OrchardCore.Liquid", + ], + Category = "Content Management", + EnabledByDependencyOnly = true +)] + [assembly: Feature( Id = "OrchardCore.Queries", Name = "Queries", Description = "Provides querying capabilities.", - Dependencies = ["OrchardCore.Liquid"], + Dependencies = + [ + "OrchardCore.Queries.Core", + ], Category = "Content Management" )] @@ -19,6 +34,9 @@ Id = "OrchardCore.Queries.Sql", Name = "SQL Queries", Description = "Introduces a way to create custom Queries in pure SQL.", - Dependencies = ["OrchardCore.Queries"], + Dependencies = + [ + "OrchardCore.Queries", + ], Category = "Content Management" )] diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/OrchardCore.Queries.csproj b/src/OrchardCore.Modules/OrchardCore.Queries/OrchardCore.Queries.csproj index 56031665529..e803bfa1c80 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/OrchardCore.Queries.csproj +++ b/src/OrchardCore.Modules/OrchardCore.Queries/OrchardCore.Queries.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Permissions.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Permissions.cs index 2b197ba176d..0331ee0732f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Permissions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Permissions.cs @@ -7,17 +7,16 @@ namespace OrchardCore.Queries; public sealed class Permissions : IPermissionProvider { public static readonly Permission ManageQueries = new("ManageQueries", "Manage queries"); - public static readonly Permission ExecuteApiAll = new("ExecuteApiAll", "Execute Api - All queries", new[] { ManageQueries }); + public static readonly Permission ExecuteApiAll = new("ExecuteApiAll", "Execute Api - All queries", [ManageQueries]); - private static readonly Permission _executeApi = new("ExecuteApi_{0}", "Execute Api - {0}", new[] { ManageQueries, ExecuteApiAll }); + private static readonly Permission _executeApi = new("ExecuteApi_{0}", "Execute Api - {0}", [ManageQueries, ExecuteApiAll]); + private readonly IQueryManager _queryManager; private readonly IEnumerable _generalPermissions = [ ManageQueries, ]; - private readonly IQueryManager _queryManager; - public Permissions(IQueryManager queryManager) { _queryManager = queryManager; @@ -31,7 +30,9 @@ public async Task> GetPermissionsAsync() ExecuteApiAll, }; - foreach (var query in await _queryManager.ListQueriesAsync()) + var queries = await _queryManager.ListQueriesAsync(true); + + foreach (var query in queries) { list.Add(CreatePermissionForQuery(query.Name)); } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs b/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs index b9fdd9c044a..467138317d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs @@ -22,12 +22,14 @@ public QueryGlobalMethodProvider() { var queryManager = serviceProvider.GetRequiredService(); var query = queryManager.GetQueryAsync(name).GetAwaiter().GetResult(); + if (query == null) { return null; } var result = queryManager.ExecuteQueryAsync(query, (IDictionary)parameters).GetAwaiter().GetResult(); + return result.Items; }), }; @@ -35,7 +37,7 @@ public QueryGlobalMethodProvider() public IEnumerable GetMethods() { - return new[] { _executeQuery }; + return [_executeQuery]; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Razor/QueryOrchardRazorHelperExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Razor/QueryOrchardRazorHelperExtensions.cs index 375e90c5f34..43ba9a73909 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Razor/QueryOrchardRazorHelperExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Razor/QueryOrchardRazorHelperExtensions.cs @@ -26,6 +26,7 @@ public static async Task QueryAsync(this IOrchardHelper orchardHelp } var result = await queryManager.ExecuteQueryAsync(query, parameters); + return result.Items; } @@ -41,6 +42,7 @@ public static async Task QueryResultsAsync(this IOrchardHelper or } var result = await queryManager.ExecuteQueryAsync(query, parameters); + return result; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs index 116f5636975..dc86d0fcd3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrchardCore.Json; @@ -15,23 +16,24 @@ namespace OrchardCore.Queries.Recipes /// /// This recipe step creates a set of queries. /// - public class QueryStep : IRecipeStepHandler + public sealed class QueryStep : IRecipeStepHandler { private readonly IQueryManager _queryManager; - private readonly IEnumerable _querySources; private readonly JsonSerializerOptions _jsonSerializerOptions; private readonly ILogger _logger; + internal readonly IStringLocalizer S; + public QueryStep( IQueryManager queryManager, - IEnumerable querySources, IOptions jsonSerializerOptions, - ILogger logger) + ILogger logger, + IStringLocalizer stringLocalizer) { _queryManager = queryManager; - _querySources = querySources; _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; _logger = logger; + S = stringLocalizer; } public async Task ExecuteAsync(RecipeExecutionContext context) @@ -43,21 +45,50 @@ public async Task ExecuteAsync(RecipeExecutionContext context) var model = context.Step.ToObject(_jsonSerializerOptions); + var queries = new List(); + foreach (var token in model.Queries.Cast()) { - var sourceName = token[nameof(Query.Source)].ToString(); - var sample = _querySources.FirstOrDefault(x => x.Name == sourceName)?.Create(); + var name = token[nameof(Query.Name)]?.GetValue(); - if (sample == null) + if (string.IsNullOrEmpty(name)) { - _logger.LogError("Could not find query source: '{QuerySource}'. The query '{QueryName}' will not be imported.", sourceName, token[nameof(Query.Name)].ToString()); + context.Errors.Add(S["Query name is missing or empty. The query will not be imported."]); continue; } - var query = token.ToObject(sample.GetType(), _jsonSerializerOptions) as Query; - await _queryManager.SaveQueryAsync(query.Name, query); + var sourceName = token[nameof(Query.Source)]?.GetValue(); + + if (string.IsNullOrEmpty(sourceName)) + { + context.Errors.Add(S["Could not find query source value. The query '{0}' will not be imported.", name]); + + continue; + } + + var query = await _queryManager.GetQueryAsync(name); + + if (query == null) + { + query = await _queryManager.NewAsync(sourceName, token); + + if (query == null) + { + context.Errors.Add(S["Could not find query source: '{0}'. The query '{1}' will not be imported.", sourceName, name]); + + continue; + } + + queries.Add(query); + } + else + { + await _queryManager.UpdateAsync(query, token); + } } + + await _queryManager.SaveAsync(queries.ToArray()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueriesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueriesDocument.cs deleted file mode 100644 index 28c91bdfccc..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueriesDocument.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using OrchardCore.Data.Documents; - -namespace OrchardCore.Queries.Services -{ - public class QueriesDocument : Document - { - public Dictionary Queries { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueryManager.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueryManager.cs deleted file mode 100644 index 0f475ca98c6..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Services/QueryManager.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using OrchardCore.Documents; - -namespace OrchardCore.Queries.Services -{ - public class QueryManager : IQueryManager - { - private readonly IDocumentManager _documentManager; - private readonly IEnumerable _querySources; - - public QueryManager(IDocumentManager documentManager, IEnumerable querySources) - { - _documentManager = documentManager; - _querySources = querySources; - } - - public async Task GetIdentifierAsync() => (await GetDocumentAsync()).Identifier; - - public async Task DeleteQueryAsync(string name) - { - var existing = await LoadDocumentAsync(); - existing.Queries.Remove(name); - await _documentManager.UpdateAsync(existing); - } - - public async Task LoadQueryAsync(string name) - { - var document = await LoadDocumentAsync(); - - if (document.Queries.TryGetValue(name, out var query)) - { - return query; - } - - return null; - } - - public async Task GetQueryAsync(string name) - { - var document = await GetDocumentAsync(); - - if (document.Queries.TryGetValue(name, out var query)) - { - return query; - } - - return null; - } - - public async Task> ListQueriesAsync() - { - return (await GetDocumentAsync()).Queries.Values.ToList(); - } - - public async Task SaveQueryAsync(string name, Query query) - { - var existing = await LoadDocumentAsync(); - existing.Queries.Remove(name); - existing.Queries[query.Name] = query; - await _documentManager.UpdateAsync(existing); - } - - /// - /// Loads the queries document from the store for updating and that should not be cached. - /// - public Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); - - /// - /// Gets the queries document from the cache for sharing and that should not be updated. - /// - public Task GetDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); - - public Task ExecuteQueryAsync(Query query, IDictionary parameters) - { - var querySource = _querySources.FirstOrDefault(q => q.Name == query.Source) - ?? throw new ArgumentException("Query source not found: " + query.Source); - - return querySource.ExecuteQueryAsync(query, parameters); - } - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs index 58f1605e8e3..e7cf900e8f9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs @@ -3,21 +3,29 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; +using OrchardCore.Entities; +using OrchardCore.Mvc.ModelBinding; +using OrchardCore.Queries.Sql.Models; using OrchardCore.Queries.Sql.ViewModels; namespace OrchardCore.Queries.Sql.Drivers { - public class SqlQueryDisplayDriver : DisplayDriver + public sealed class SqlQueryDisplayDriver : DisplayDriver { - protected readonly IStringLocalizer S; + internal readonly IStringLocalizer S; public SqlQueryDisplayDriver(IStringLocalizer stringLocalizer) { S = stringLocalizer; } - public override IDisplayResult Display(SqlQuery query, IUpdateModel updater) + public override IDisplayResult Display(Query query, IUpdateModel updater) { + if (query.Source != SqlQuerySource.SourceName) + { + return null; + } + return Combine( Dynamic("SqlQuery_SummaryAdmin", model => { @@ -30,35 +38,51 @@ public override IDisplayResult Display(SqlQuery query, IUpdateModel updater) ); } - public override IDisplayResult Edit(SqlQuery query, IUpdateModel updater) + public override IDisplayResult Edit(Query query, IUpdateModel updater) { - return Initialize("SqlQuery_Edit", model => + if (query.Source != SqlQuerySource.SourceName) { - model.Query = query.Template; - model.ReturnDocuments = query.ReturnDocuments; + return null; + } + + return Initialize("SqlQuery_Edit", async model => + { + model.ReturnDocuments = query.ReturnContentItems; + + var metadata = query.As(); + model.Query = metadata.Template; // Extract query from the query string if we come from the main query editor. - if (string.IsNullOrEmpty(query.Template)) + if (string.IsNullOrEmpty(metadata.Template)) { - updater.TryUpdateModelAsync(model, "", m => m.Query); + await updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); } + }).Location("Content:5"); } - public override async Task UpdateAsync(SqlQuery model, IUpdateModel updater) + public override async Task UpdateAsync(Query query, IUpdateModel updater) { + if (query.Source != SqlQuerySource.SourceName) + { + return null; + } + var viewModel = new SqlQueryViewModel(); await updater.TryUpdateModelAsync(viewModel, Prefix, m => m.Query, m => m.ReturnDocuments); - model.Template = viewModel.Query; - model.ReturnDocuments = viewModel.ReturnDocuments; - - if (string.IsNullOrWhiteSpace(model.Template)) + if (string.IsNullOrWhiteSpace(viewModel.Query)) { - updater.ModelState.AddModelError(nameof(model.Template), S["The query field is required"]); + updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); } - return Edit(model, updater); + query.ReturnContentItems = viewModel.ReturnDocuments; + query.Put(new SqlQueryMetadata() + { + Template = viewModel.Query, + }); + + return Edit(query, updater); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs index 811c97aba68..1df672f97d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs @@ -13,6 +13,8 @@ using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Resolvers; using OrchardCore.ContentManagement.GraphQL.Queries; +using OrchardCore.Entities; +using OrchardCore.Queries.Sql.Models; namespace OrchardCore.Queries.Sql.GraphQL.Queries { @@ -25,14 +27,18 @@ public class SqlQueryFieldTypeProvider : ISchemaBuilder private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; - public SqlQueryFieldTypeProvider(IHttpContextAccessor httpContextAccessor, ILogger logger) + public SqlQueryFieldTypeProvider( + IHttpContextAccessor httpContextAccessor, + ILogger logger) { _httpContextAccessor = httpContextAccessor; _logger = logger; } + public Task GetIdentifierAsync() { var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + return queryManager.GetIdentifierAsync(); } @@ -40,12 +46,14 @@ public async Task BuildAsync(ISchema schema) { var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - var queries = await queryManager.ListQueriesAsync(); + var queries = await queryManager.ListQueriesBySourceAsync(SqlQuerySource.SourceName); - foreach (var query in queries.OfType()) + foreach (var query in queries) { if (string.IsNullOrWhiteSpace(query.Schema)) + { continue; + } var name = query.Name; @@ -60,9 +68,11 @@ public async Task BuildAsync(ISchema schema) var type = querySchema["type"].ToString(); FieldType fieldType; + var metadata = query.As(); + var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; - if (query.ReturnDocuments && type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) + if (query.ReturnContentItems && type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) { var contentType = type.Remove(0, 12); fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); @@ -84,7 +94,7 @@ public async Task BuildAsync(ISchema schema) } } - private static FieldType BuildSchemaBasedFieldType(SqlQuery query, JsonNode querySchema, string fieldTypeName) + private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) { var properties = querySchema["properties"]?.AsObject(); if (properties == null) @@ -92,7 +102,7 @@ private static FieldType BuildSchemaBasedFieldType(SqlQuery query, JsonNode quer return null; } - var typetype = new ObjectGraphType + var typeType = new ObjectGraphType { Name = fieldTypeName }; @@ -118,7 +128,7 @@ private static FieldType BuildSchemaBasedFieldType(SqlQuery query, JsonNode quer }), }; field.Metadata.Add("Name", name); - typetype.AddField(field); + typeType.AddField(field); } else if (type == "integer") { @@ -133,9 +143,9 @@ private static FieldType BuildSchemaBasedFieldType(SqlQuery query, JsonNode quer return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); }), }; - + field.Metadata.Add("Name", name); - typetype.AddField(field); + typeType.AddField(field); } } @@ -147,23 +157,24 @@ private static FieldType BuildSchemaBasedFieldType(SqlQuery query, JsonNode quer Name = fieldTypeName, Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = new ListGraphType(typetype), + ResolvedType = new ListGraphType(typeType), Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeof(ListGraphType>) }; async ValueTask ResolveAsync(IResolveFieldContext context) { - var queryManager = context.RequestServices.GetService(); - var iquery = await queryManager.GetQueryAsync(query.Name); + var queryManager = context.RequestServices.GetRequiredService(); + + var iQuery = await queryManager.GetQueryAsync(query.Name); var parameters = context.GetArgument("parameters"); - var queryParameters = parameters != null ? - JConvert.DeserializeObject>(parameters) - : []; + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - var result = await queryManager.ExecuteQueryAsync(iquery, queryParameters); + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); return result.Items; } @@ -171,10 +182,10 @@ async ValueTask ResolveAsync(IResolveFieldContext context) return fieldType; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, SqlQuery query, string fieldTypeName) + private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) { - var typetype = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); - if (typetype == null) + var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); + if (typeType == null) { return null; } @@ -184,26 +195,27 @@ private static FieldType BuildContentTypeFieldType(ISchema schema, string conten Arguments = new QueryArguments( new QueryArgument { Name = "parameters" } ), - Name = fieldTypeName, Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = typetype.ResolvedType, + ResolvedType = typeType.ResolvedType, Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typetype.Type + Type = typeType.Type }; async ValueTask ResolveAsync(IResolveFieldContext context) { - var queryManager = context.RequestServices.GetService(); - var iquery = await queryManager.GetQueryAsync(query.Name); + var queryManager = context.RequestServices.GetRequiredService(); + + var iQuery = await queryManager.GetQueryAsync(query.Name); var parameters = context.GetArgument("parameters"); - var queryParameters = parameters != null ? - JConvert.DeserializeObject>(parameters) - : []; + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; + + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - var result = await queryManager.ExecuteQueryAsync(iquery, queryParameters); return result.Items; } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Migrations/SqlQueryMigrations.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Migrations/SqlQueryMigrations.cs new file mode 100644 index 00000000000..e400933f4a9 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Migrations/SqlQueryMigrations.cs @@ -0,0 +1,15 @@ +using OrchardCore.Data.Migration; +using OrchardCore.Queries.Core; + +namespace OrchardCore.Queries.Sql.Migrations; + +public sealed class SqlQueryMigrations : DataMigration +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")] + public int Create() + { + QueriesDocumentMigrationHelper.Migrate(SqlQuerySource.SourceName); + + return 1; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Models/SqlQueryMetadata.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Models/SqlQueryMetadata.cs new file mode 100644 index 00000000000..83a4172069b --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Models/SqlQueryMetadata.cs @@ -0,0 +1,6 @@ +namespace OrchardCore.Queries.Sql.Models; + +public sealed class SqlQueryMetadata +{ + public string Template { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SQLQueryResults.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SQLQueryResults.cs new file mode 100644 index 00000000000..9d22b0d1cbd --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SQLQueryResults.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using OrchardCore.Queries; + +namespace OrchardCore.Data; + +public class SQLQueryResults : IQueryResults +{ + public IEnumerable Items { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuery.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuery.cs deleted file mode 100644 index 533aa81a07d..00000000000 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuery.cs +++ /dev/null @@ -1,15 +0,0 @@ -using OrchardCore.ContentManagement; - -namespace OrchardCore.Queries.Sql -{ - public class SqlQuery : Query - { - public SqlQuery() : base("Sql") - { - } - - public string Template { get; set; } - public bool ReturnDocuments { get; set; } - public override bool ResultsOfType() => ReturnDocuments ? typeof(T) == typeof(ContentItem) : base.ResultsOfType(); - } -} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQueryHandler.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQueryHandler.cs new file mode 100644 index 00000000000..1e930f31029 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQueryHandler.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using OrchardCore.Entities; +using OrchardCore.Queries.Core; +using OrchardCore.Queries.Sql.Models; + +namespace OrchardCore.Queries.Sql; + +public sealed class SqlQueryHandler : QueryHandlerBase +{ + public override Task InitializingAsync(InitializingQueryContext context) + => UpdateQueryAsync(context); + + public override Task UpdatingAsync(UpdatingQueryContext context) + => UpdateQueryAsync(context); + + private static Task UpdateQueryAsync(DataQueryContextBase context) + { + if (context.Query.Source != SqlQuerySource.SourceName) + { + return Task.CompletedTask; + } + + var template = context.Data[nameof(SqlQueryMetadata.Template)]?.GetValue(); + + if (!string.IsNullOrEmpty(template)) + { + var metadata = context.Query.As(); + + metadata.Template = template; + context.Query.Put(metadata); + }; + + context.Query.CanReturnContentItems = true; + + return Task.CompletedTask; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs index c26dd1254e9..c5a1a2a3e91 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; @@ -11,14 +10,18 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Records; using OrchardCore.Data; +using OrchardCore.Entities; using OrchardCore.Json; using OrchardCore.Liquid; +using OrchardCore.Queries.Sql.Models; using YesSql; namespace OrchardCore.Queries.Sql { - public class SqlQuerySource : IQuerySource + public sealed class SqlQuerySource : IQuerySource { + public const string SourceName = "Sql"; + private readonly ILiquidTemplateManager _liquidTemplateManager; private readonly IDbConnectionAccessor _dbConnectionAccessor; private readonly ISession _session; @@ -39,26 +42,23 @@ public SqlQuerySource( _templateOptions = templateOptions.Value; } - public string Name => "Sql"; - - public Query Create() - { - return new SqlQuery(); - } + public string Name + => SourceName; public async Task ExecuteQueryAsync(Query query, IDictionary parameters) { - var sqlQuery = query as SqlQuery; + var metadata = query.As(); + var sqlQueryResults = new SQLQueryResults(); - var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(sqlQuery.Template, NullEncoder.Default, + var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(metadata.Template, NullEncoder.Default, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); var dialect = _session.Store.Configuration.SqlDialect; if (!SqlParser.TryParse(tokenizedQuery, _session.Store.Configuration.Schema, dialect, _session.Store.Configuration.TablePrefix, parameters, out var rawQuery, out var messages)) { - sqlQueryResults.Items = Array.Empty(); + sqlQueryResults.Items = []; return sqlQueryResults; } @@ -67,16 +67,14 @@ public async Task ExecuteQueryAsync(Query query, IDictionary documentIds; - using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); var queryResult = await connection.QueryAsync(rawQuery, parameters, transaction); string column = null; - documentIds = queryResult.Select(row => + var documentIds = queryResult.Select(row => { var rowDictionary = (IDictionary)row; @@ -88,8 +86,7 @@ public async Task ExecuteQueryAsync(Query query, IDictionary kv.Value is long).Key + column = rowDictionary.FirstOrDefault(kv => kv.Value is long).Key ?? rowDictionary.First().Key; } } @@ -97,18 +94,16 @@ public async Task ExecuteQueryAsync(Query query, IDictionary(documentIds.ToArray()); + sqlQueryResults.Items = await _session.GetAsync(documentIds); return sqlQueryResults; } else { - IEnumerable queryResults; - using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); - queryResults = await connection.QueryAsync(rawQuery, parameters, transaction); + var queryResults = await connection.QueryAsync(rawQuery, parameters, transaction); var results = new List(); foreach (var document in queryResults) @@ -117,6 +112,7 @@ public async Task ExecuteQueryAsync(Query query, IDictionary(); services.AddScoped, SqlQueryDisplayDriver>(); - services.AddScoped(); - services.AddScoped(); + services.AddQuerySource(SqlQuerySource.SourceName); - // Allows to serialize 'SqlQuery' from its base type. - services.AddJsonDerivedTypeInfo(); + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs index 8b4b4f56893..c601dbe8c8b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs @@ -6,11 +6,11 @@ using OrchardCore.Liquid; using OrchardCore.Modules; using OrchardCore.Navigation; +using OrchardCore.Queries.Core.Services; using OrchardCore.Queries.Deployment; using OrchardCore.Queries.Drivers; using OrchardCore.Queries.Liquid; using OrchardCore.Queries.Recipes; -using OrchardCore.Queries.Services; using OrchardCore.Recipes; using OrchardCore.Scripting; using OrchardCore.Security.Permissions; @@ -25,10 +25,17 @@ public sealed class Startup : StartupBase public override void ConfigureServices(IServiceCollection services) { services.AddScoped(); - services.AddScoped(); services.AddScoped, QueryDisplayDriver>(); - services.AddRecipeExecutionStep(); services.AddScoped(); + } + } + + [Feature("OrchardCore.Queries.Core")] + public sealed class CoreStartup : StartupBase + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddRecipeExecutionStep(); services.AddDeployment(); services.AddSingleton(); @@ -40,10 +47,14 @@ public override void ConfigureServices(IServiceCollection services) var liquidTemplateContext = (LiquidTemplateContext)context; var queryManager = liquidTemplateContext.Services.GetRequiredService(); - return FluidValue.Create(await queryManager.GetQueryAsync(name), context.Options); + var query = await queryManager.GetQueryAsync(name); + + return FluidValue.Create(query, context.Options); }); }) .AddLiquidFilter("query"); + + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesCreateViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesCreateViewModel.cs index b06ff69b564..c09fd5a6aa7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesCreateViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesCreateViewModel.cs @@ -1,8 +1,8 @@ -namespace OrchardCore.Queries.ViewModels +namespace OrchardCore.Queries.ViewModels; + +public class QueriesCreateViewModel { - public class QueriesCreateViewModel - { - public string SourceName { get; set; } - public dynamic Editor { get; set; } - } + public string SourceName { get; set; } + + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesEditViewModel.cs index fcb75bec51d..5c2d26b957f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesEditViewModel.cs @@ -1,8 +1,8 @@ -namespace OrchardCore.Queries.ViewModels +namespace OrchardCore.Queries.ViewModels; + +public class QueriesEditViewModel { - public class QueriesEditViewModel : QueriesCreateViewModel - { - public string Name { get; set; } - public string Schema { get; set; } - } + public string Name { get; set; } + + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs index b814cc97a65..e29bd2621b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs @@ -1,9 +1,17 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ModelBinding; + namespace OrchardCore.Queries.ViewModels { public class QueryBasedContentDeploymentStepViewModel { public string QueryName { get; set; } + public string QueryParameters { get; set; } = "{}"; + public bool ExportAsSetupRecipe { get; set; } + + [BindNever] + public IEnumerable Queries { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Views/Items/QueryBasedContentDeploymentStep.Fields.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Queries/Views/Items/QueryBasedContentDeploymentStep.Fields.Edit.cshtml index 4c04a5feb20..4eb54317535 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Views/Items/QueryBasedContentDeploymentStep.Fields.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Views/Items/QueryBasedContentDeploymentStep.Fields.Edit.cshtml @@ -1,7 +1,6 @@ @using OrchardCore.Queries.ViewModels @model QueryBasedContentDeploymentStepViewModel @inject OrchardCore.ContentManagement.Metadata.IContentDefinitionManager ContentDefinitionManager -@inject OrchardCore.Queries.IQueryManager QueryManager @@ -23,7 +22,7 @@