From 2c1a310aae01be753034114af0c2238bb2ae6bf2 Mon Sep 17 00:00:00 2001 From: Georg von Kries Date: Tue, 26 Nov 2024 19:51:11 +0100 Subject: [PATCH] Fix GraphQL schema localization. (#17041) --------- Co-authored-by: Mike Alhayek --- .../GraphQL/AliasInputObjectType.cs | 3 +- .../Services/SchemaService.cs | 15 +++--- .../Views/Admin/Index.cshtml | 3 +- .../GraphQL/AutorouteInputObjectType.cs | 3 +- .../GraphQL/Fields/ContentFieldsProvider.cs | 26 ++++++---- .../ContentPickerFieldQueryObjectType.cs | 7 +-- .../GraphQL/Types/LinkFieldQueryObjectType.cs | 14 +++-- .../GraphQL/LocalizationInputObjectType.cs | 3 +- .../GraphQL/BagPartQueryObjectType.cs | 2 +- .../GraphQL/FlowAlignmentEnum.cs | 13 ++--- .../GraphQL/FlowPartQueryObjectType.cs | 2 +- .../GraphQL/LayerQueryObjectType.cs | 13 ++--- .../GraphQL/LayerWidgetQueryObjectType.cs | 11 ++-- .../GraphQL/ContainedInputObjectType.cs | 3 +- .../GraphQL/ListQueryObjectType.cs | 6 +-- .../GraphQL/CultureQueryObjectType.cs | 12 +++-- .../GraphQL/MediaAssetObjectType.cs | 13 +++-- .../GraphQL/MediaFieldQueryObjectType.cs | 34 +++++++++---- .../GraphQL/MenuItemsListQueryObjectType.cs | 5 +- .../Sql/GraphQL/SqlQueryFieldTypeProvider.cs | 13 +++-- .../GraphQL/ElasticQueryFieldTypeProvider.cs | 13 +++-- .../GraphQL/LuceneQueryFieldTypeProvider.cs | 13 +++-- .../GraphQL/MetaEntryQueryObjectType.cs | 17 ++++--- .../GraphQL/SeoMetaQueryObjectType.cs | 42 +++++++++------ .../GraphQL/TaxonomyFieldQueryObjectType.cs | 11 ++-- .../GraphQL/TaxonomyPartQueryObjectType.cs | 5 +- .../Queries/WhereInputObjectGraphType.cs | 51 +++++++++++-------- .../Queries/ContentItemsFieldType.cs | 37 +++++++------- .../Queries/ContentTypeQuery.cs | 5 +- .../Queries/PublicationStatusGraphType.cs | 13 ++--- .../Queries/Types/ContentItemOrderByInput.cs | 9 ++-- .../Queries/Types/ContentItemType.cs | 25 ++++----- .../Queries/Types/ContentItemWhereInput.cs | 37 +++++++++----- .../Types/DynamicContentTypeBuilder.cs | 2 +- .../DynamicContentTypeWhereInputBuilder.cs | 4 +- .../Types/DynamicPartWhereInputGraphType.cs | 4 +- .../GraphQL/ContentItemsFieldTypeTests.cs | 49 ++++++++++++++---- 37 files changed, 333 insertions(+), 205 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs index e6f61ee738e..d653c4d25e8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs @@ -7,7 +7,8 @@ namespace OrchardCore.Alias.GraphQL; public class AliasInputObjectType : WhereInputObjectGraphType { - public AliasInputObjectType(IStringLocalizer S) + public AliasInputObjectType(IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "AliasPartInput"; Description = S["the alias part of the content item"]; diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs index bc13d9055f5..f6fe8b746b9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Globalization; using GraphQL; using GraphQL.MicrosoftDI; using GraphQL.Types; @@ -13,8 +14,7 @@ public sealed class SchemaService : ISchemaFactory private readonly IServiceProvider _serviceProvider; private readonly SemaphoreSlim _schemaGenerationSemaphore = new(1, 1); private readonly ConcurrentDictionary _identifiers = new(); - - private ISchema _schema; + private readonly ConcurrentDictionary _schemas = new(); public SchemaService( IEnumerable schemaBuilders, @@ -27,6 +27,7 @@ public SchemaService( public async Task GetSchemaAsync() { var hasChanged = false; + var culture = CultureInfo.CurrentUICulture; foreach (var builder in _schemaBuilders) { @@ -37,9 +38,9 @@ public async Task GetSchemaAsync() } } - if (_schema is object && !hasChanged) + if (!hasChanged && _schemas.TryGetValue(culture, out var existingSchema)) { - return _schema; + return existingSchema; } await _schemaGenerationSemaphore.WaitAsync(); @@ -55,9 +56,9 @@ public async Task GetSchemaAsync() } } - if (_schema is object && !hasChanged) + if (!hasChanged && _schemas.TryGetValue(culture, out existingSchema)) { - return _schema; + return existingSchema; } var serviceProvider = ShellScope.Services; @@ -115,7 +116,7 @@ public async Task GetSchemaAsync() schema.Initialize(); - return _schema = schema; + return _schemas[culture] = schema; } finally { diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Views/Admin/Index.cshtml b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Views/Admin/Index.cshtml index d6c7ded08ce..3d5f792e47c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Views/Admin/Index.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Views/Admin/Index.cshtml @@ -1,3 +1,4 @@ +@using System.Globalization -
+
diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs index bf996ee1e0b..74c2e418614 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs @@ -7,7 +7,8 @@ namespace OrchardCore.Autoroute.GraphQL; public class AutorouteInputObjectType : WhereInputObjectGraphType { - public AutorouteInputObjectType(IStringLocalizer S) + public AutorouteInputObjectType(IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "AutoroutePartInput"; Description = S["the custom URL part of the content item"]; diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs index 6954860e83b..8f199d9a9bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs @@ -1,6 +1,7 @@ using System.Collections.Frozen; using GraphQL.Resolvers; using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL.Queries.Types; using OrchardCore.ContentFields.Fields; using OrchardCore.ContentFields.Indexing.SQL; @@ -18,7 +19,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(BooleanField), new FieldTypeDescriptor { - Description = "Boolean field", + Description = S => S["Boolean field"], FieldType = typeof(BooleanGraphType), ResolvedType = new BooleanGraphType(), UnderlyingType = typeof(BooleanField), @@ -31,7 +32,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(DateField), new FieldTypeDescriptor { - Description = "Date field", + Description = S => S["Date field"], FieldType = typeof(DateGraphType), ResolvedType = new DateGraphType(), UnderlyingType = typeof(DateField), @@ -44,7 +45,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(DateTimeField), new FieldTypeDescriptor { - Description = "Date & time field", + Description = S => S["Date & time field"], FieldType = typeof(DateTimeGraphType), ResolvedType = new DateTimeGraphType(), UnderlyingType = typeof(DateTimeField), @@ -57,7 +58,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(NumericField), new FieldTypeDescriptor { - Description = "Numeric field", + Description = S => S["Numeric field"], FieldType = typeof(DecimalGraphType), ResolvedType = new DecimalGraphType(), UnderlyingType = typeof(NumericField), @@ -70,7 +71,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(TextField), new FieldTypeDescriptor { - Description = "Text field", + Description = S => S["Text field"], FieldType = typeof(StringGraphType), ResolvedType = new StringGraphType(), UnderlyingType = typeof(TextField), @@ -83,7 +84,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(TimeField), new FieldTypeDescriptor { - Description = "Time field", + Description = S => S["Time field"], FieldType = typeof(TimeSpanGraphType), ResolvedType = new TimeSpanGraphType(), UnderlyingType = typeof(TimeField), @@ -96,7 +97,7 @@ public class ContentFieldsProvider : IContentFieldProvider nameof(MultiTextField), new FieldTypeDescriptor { - Description = "Multi text field", + Description = S => S["Multi text field"], FieldType = typeof(ListGraphType), ResolvedType = new ListGraphType(new StringGraphType()), UnderlyingType = typeof(MultiTextField), @@ -105,6 +106,13 @@ public class ContentFieldsProvider : IContentFieldProvider } }.ToFrozenDictionary(); + protected readonly IStringLocalizer S; + + public ContentFieldsProvider(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } + public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName) { if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldDescriptor)) @@ -115,7 +123,7 @@ public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, stri return new FieldType { Name = customFieldName ?? schema.NameConverter.NameForField(field.Name, null), - Description = fieldDescriptor.Description, + Description = fieldDescriptor.Description(S), Type = fieldDescriptor.FieldType, ResolvedType = fieldDescriptor.ResolvedType, Resolver = new FuncFieldResolver(context => @@ -161,7 +169,7 @@ public bool HasFieldIndex(ContentPartFieldDefinition field) private sealed class FieldTypeDescriptor { - public string Description { get; set; } + public Func Description { get; set; } public Type FieldType { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs index df80bd7e94f..7d1fb715466 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs @@ -1,5 +1,6 @@ using GraphQL; using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL; using OrchardCore.ContentFields.Fields; using OrchardCore.ContentManagement; @@ -10,12 +11,12 @@ namespace OrchardCore.ContentFields.GraphQL; public class ContentPickerFieldQueryObjectType : ObjectGraphType { - public ContentPickerFieldQueryObjectType() + public ContentPickerFieldQueryObjectType(IStringLocalizer S) { Name = nameof(ContentPickerField); Field, IEnumerable>("contentItemIds") - .Description("content item ids") + .Description(S["content item ids"]) .PagingArguments() .Resolve(x => { @@ -23,7 +24,7 @@ public ContentPickerFieldQueryObjectType() }); Field, IEnumerable>("contentItems") - .Description("the content items") + .Description(S["the content items"]) .PagingArguments() .ResolveAsync(x => { diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs index 50c239be081..d6349f20c1b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs @@ -1,16 +1,22 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.ContentFields.Fields; namespace OrchardCore.ContentFields.GraphQL.Types; public class LinkFieldQueryObjectType : ObjectGraphType { - public LinkFieldQueryObjectType() + public LinkFieldQueryObjectType(IStringLocalizer S) { Name = nameof(LinkField); - Field(x => x.Url, nullable: true).Description("the url of the link"); - Field(x => x.Text, nullable: true).Description("the text of the link"); - Field(x => x.Target, nullable: true).Description("the target of the link"); + Field(x => x.Url, nullable: true) + .Description(S["the url of the link"]); + + Field(x => x.Text, nullable: true) + .Description(S["the text of the link"]); + + Field(x => x.Target, nullable: true) + .Description(S["the target of the link"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs index 11597400908..5c7cfade619 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs @@ -7,7 +7,8 @@ namespace OrchardCore.ContentLocalization.GraphQL; public class LocalizationInputObjectType : WhereInputObjectGraphType { - public LocalizationInputObjectType(IStringLocalizer S) + public LocalizationInputObjectType(IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "LocalizationInputObjectType"; Description = S["the localization part of the content item"]; diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs index 7a42b7f474a..37bc0fa8545 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs @@ -15,7 +15,7 @@ public BagPartQueryObjectType(IStringLocalizer S) Description = S["A BagPart allows to add content items directly within another content item"]; Field, IEnumerable>("contentItems") - .Description("the content items") + .Description(S["the content items"]) .PagingArguments() .Resolve(x => x.Page(x.Source.ContentItems)); } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs index 52b78698012..628ffdbcc65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs @@ -1,17 +1,18 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; namespace OrchardCore.Flows.GraphQL; public class FlowAlignmentEnum : EnumerationGraphType { - public FlowAlignmentEnum() + public FlowAlignmentEnum(IStringLocalizer S) { Name = "FlowAlignment"; - Description = "The widget alignment."; - Add("Left", 0, "Left alignment."); - Add("Center", 1, "Center alignment."); - Add("Right", 2, "Right alignment."); - Add("Justify", 3, "Justify alignment."); + Description = S["The widget alignment."]; + Add("Left", 0, S["Left alignment."]); + Add("Center", 1, S["Center alignment."]); + Add("Right", 2, S["Right alignment."]); + Add("Justify", 3, S["Justify alignment."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs index 04ef713cd5d..b25b1c9cb9a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs @@ -13,7 +13,7 @@ public FlowPartQueryObjectType(IStringLocalizer S) Description = S["A FlowPart allows to add content items directly within another content item"]; Field>("widgets") - .Description("The widgets.") + .Description(S["The widgets."]) .Resolve(context => context.Source.Widgets); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs index 58593fcf4e4..f12afbacaf2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs @@ -2,6 +2,7 @@ using GraphQL; using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL; using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.GraphQL.Queries; @@ -14,23 +15,23 @@ namespace OrchardCore.Layers.GraphQL; public class LayerQueryObjectType : ObjectGraphType { - public LayerQueryObjectType() + public LayerQueryObjectType(IStringLocalizer S) { Name = "Layer"; Field(layer => layer.Name) - .Description("The name of the layer."); + .Description(S["The name of the layer."]); Field, IEnumerable>("layerrule") - .Description("The rule that activates the layer.") + .Description(S["The rule that activates the layer."]) .Resolve(ctx => ctx.Source.LayerRule.Conditions); Field(layer => layer.Description) - .Description("The description of the layer."); + .Description(S["The description of the layer."]); Field, IEnumerable>("widgets") - .Description("The widgets for this layer.") - .Argument("status", "publication status of the widgets") + .Description(S["The widgets for this layer."]) + .Argument("status", S["publication status of the widgets"]) .ResolveLockedAsync(GetWidgetsForLayerAsync); async ValueTask> GetWidgetsForLayerAsync(IResolveFieldContext context) diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs index 93fe4be7ff4..0e01c011a16 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs @@ -1,4 +1,5 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Layers.Models; @@ -7,24 +8,24 @@ namespace OrchardCore.Layers.GraphQL; public class LayerWidgetQueryObjectType : ObjectGraphType { - public LayerWidgetQueryObjectType() + public LayerWidgetQueryObjectType(IStringLocalizer S) { Name = "LayerWidget"; Field("zone") - .Description("The name of the widget's zone.") + .Description(S["The name of the widget's zone."]) .Resolve(context => context.Source.As()?.Zone); Field("position") - .Description("The position of the widget in the zone.") + .Description(S["The position of the widget in the zone."]) .Resolve(context => context.Source.As()?.Position); Field("renderTitle") - .Description("Whether to render the widget's title.") + .Description(S["Whether to render the widget's title."]) .Resolve(context => context.Source.As()?.RenderTitle); Field("widget") - .Description("A widget on this layer.") + .Description(S["A widget on this layer."]) .Resolve(context => context.Source); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs index 4f242ab09c7..b0f3e498d94 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs @@ -7,7 +7,8 @@ namespace OrchardCore.Lists.GraphQL; public class ContainedInputObjectType : WhereInputObjectGraphType { - public ContainedInputObjectType(IStringLocalizer S) + public ContainedInputObjectType(IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "ContainedPartInput"; Description = S["the list part of the content item"]; diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs index 3dc1b2b01bd..cd2d6bbe238 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs @@ -22,9 +22,9 @@ public ListQueryObjectType(IStringLocalizer S) Description = S["Represents a collection of content items."]; Field, IEnumerable>("contentItems") - .Description("the content items") - .Argument("first", "the first n elements (10 by default)", config => config.DefaultValue = 10) - .Argument("skip", "the number of elements to skip", config => config.DefaultValue = 0) + .Description(S["the content items"]) + .Argument("first", S["the first n elements (10 by default)"], config => config.DefaultValue = 10) + .Argument("skip", S["the number of elements to skip"], config => config.DefaultValue = 0) // Important to use ResolveLockedAsync to prevent concurrency error on database query, when using nested content items with List part .ResolveLockedAsync(async g => { diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs index b436eb8b2b8..32df10b50a2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs @@ -1,4 +1,5 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; namespace OrchardCore.Localization.GraphQL; @@ -10,11 +11,16 @@ public class CultureQueryObjectType : ObjectGraphType /// /// Creates a new instance of . /// - public CultureQueryObjectType() + public CultureQueryObjectType(IStringLocalizer S) { Name = "SiteCulture"; - Field("culture").Description("The culture code.").Resolve(context => context.Source.Culture); - Field("default").Description("Whether this is the default culture.").Resolve(context => context.Source.IsDefault); + Field("culture") + .Description(S["The culture code."]) + .Resolve(context => context.Source.Culture); + + Field("default") + .Description(S["Whether this is the default culture."]) + .Resolve(context => context.Source.IsDefault); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs index ec6caaa963e..3565eed7b74 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs @@ -1,19 +1,20 @@ using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using OrchardCore.FileStorage; namespace OrchardCore.Media.GraphQL; public class MediaAssetObjectType : ObjectGraphType { - public MediaAssetObjectType() + public MediaAssetObjectType(IStringLocalizer S) { Name = "MediaAsset"; - Field(file => file.Name).Description("The name of the asset."); + Field(file => file.Name).Description(S["The name of the asset."]); Field("path") - .Description("The url to the asset.") + .Description(S["The url to the asset."]) .Resolve(x => { var path = x.Source.Path; @@ -21,9 +22,11 @@ public MediaAssetObjectType() return mediaFileStore.MapPathToPublicUrl(path); }); - Field(file => file.Length).Description("The length of the file."); + Field(file => file.Length) + .Description(S["The length of the file."]); + Field("lastModifiedUtc") - .Description("The date and time in UTC when the asset was last modified.") + .Description(S["The date and time in UTC when the asset was last modified."]) .Resolve(file => file.Source.LastModifiedUtc); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs index b3ed50383f1..d98e9c10e75 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs @@ -1,5 +1,6 @@ using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL; using OrchardCore.Media.Fields; @@ -7,14 +8,14 @@ namespace OrchardCore.Media.GraphQL; public class MediaFieldQueryObjectType : ObjectGraphType { - public MediaFieldQueryObjectType() + public MediaFieldQueryObjectType(IStringLocalizer S) { Name = nameof(MediaField); if (MediaAppContextSwitches.EnableLegacyMediaFields) { Field, IEnumerable>("paths") - .Description("the media paths") + .Description(S["the media paths"]) .PagingArguments() .Resolve(x => { @@ -27,7 +28,7 @@ public MediaFieldQueryObjectType() }); Field, IEnumerable>("fileNames") - .Description("the media file names") + .Description(S["the media file names"]) .PagingArguments() .Resolve(x => { @@ -40,7 +41,7 @@ public MediaFieldQueryObjectType() }); Field, IEnumerable>("urls") - .Description("the absolute urls of the media items") + .Description(S["the absolute urls of the media items"]) .PagingArguments() .Resolve(x => { @@ -55,7 +56,7 @@ public MediaFieldQueryObjectType() }); Field, IEnumerable>("mediatexts") - .Description("the media texts") + .Description(S["the media texts"]) .PagingArguments() .Resolve(x => { @@ -68,7 +69,7 @@ public MediaFieldQueryObjectType() } Field, IEnumerable>("files") - .Description("the files of the media items") + .Description(S["the files of the media items"]) .PagingArguments() .Resolve(x => { @@ -101,12 +102,23 @@ public MediaFieldQueryObjectType() public sealed class MediaFileItemType : ObjectGraphType { - public MediaFileItemType() + public MediaFileItemType(IStringLocalizer S) { - Field("fileName").Description("the file name of the media file item").Resolve(x => x.Source.FileName); - Field("path").Description("the path of the media file item").Resolve(x => x.Source.Path); - Field("url").Description("the url name of the media file item").Resolve(x => x.Source.Url); - Field("mediaText").Description("the media text of the file item").Resolve(x => x.Source.MediaText); + Field("fileName") + .Description(S["the file name of the media file item"]) + .Resolve(x => x.Source.FileName); + + Field("path") + .Description(S["the path of the media file item"]) + .Resolve(x => x.Source.Path); + + Field("url") + .Description(S["the url name of the media file item"]) + .Resolve(x => x.Source.Url); + + Field("mediaText") + .Description(S["the media text of the file item"]) + .Resolve(x => x.Source.MediaText); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs index 2030c2c94ce..af79c82087b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs @@ -1,16 +1,17 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.Menu.Models; namespace OrchardCore.Menu.GraphQL; public class MenuItemsListQueryObjectType : ObjectGraphType { - public MenuItemsListQueryObjectType() + public MenuItemsListQueryObjectType(IStringLocalizer S) { Name = "MenuItemsListPart"; Field>("menuItems") - .Description("The menu items.") + .Description(S["The menu items."]) .Resolve(context => context.Source.MenuItems); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs index 98d9893d656..d38a95e3d04 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs @@ -5,6 +5,7 @@ using GraphQL.Types; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Resolvers; @@ -23,11 +24,15 @@ public sealed class SqlQueryFieldTypeProvider : ISchemaBuilder private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; + internal readonly IStringLocalizer S; + public SqlQueryFieldTypeProvider( IHttpContextAccessor httpContextAccessor, + IStringLocalizer stringLocalizer, ILogger logger) { _httpContextAccessor = httpContextAccessor; + S = stringLocalizer; _logger = logger; } @@ -90,7 +95,7 @@ public async Task BuildAsync(ISchema schema) } } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) { var properties = querySchema["properties"]?.AsObject(); if (properties == null) @@ -152,7 +157,7 @@ private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySc ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = new ListGraphType(typeType), Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeof(ListGraphType>) @@ -178,7 +183,7 @@ async ValueTask ResolveAsync(IResolveFieldContext context) return fieldType; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + private FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) { var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); if (typeType == null) @@ -192,7 +197,7 @@ private static FieldType BuildContentTypeFieldType(ISchema schema, string conten new QueryArgument { Name = "parameters" } ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = typeType.ResolvedType, Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeType.Type diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs index 11440892e85..69c3af6a794 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs @@ -5,6 +5,7 @@ using GraphQL.Types; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Resolvers; @@ -21,11 +22,15 @@ public sealed class ElasticQueryFieldTypeProvider : ISchemaBuilder private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; + internal readonly IStringLocalizer S; + public ElasticQueryFieldTypeProvider( IHttpContextAccessor httpContextAccessor, + IStringLocalizer stringLocalizer, ILogger logger) { _httpContextAccessor = httpContextAccessor; + S = stringLocalizer; _logger = logger; } @@ -87,7 +92,7 @@ public async Task BuildAsync(ISchema schema) } } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) { var properties = querySchema["properties"]?.AsObject(); if (properties == null) @@ -150,7 +155,7 @@ private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySc new QueryArgument { Name = "parameters" } ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = new ListGraphType(typeType), Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeof(ListGraphType>) @@ -176,7 +181,7 @@ async ValueTask ResolveAsync(IResolveFieldContext context) return fieldType; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + private FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) { var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); @@ -191,7 +196,7 @@ private static FieldType BuildContentTypeFieldType(ISchema schema, string conten new QueryArgument { Name = "parameters" } ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = typeType.ResolvedType, Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeType.Type diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs index c7b5cb68591..85dc4101741 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs @@ -5,6 +5,7 @@ using GraphQL.Types; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Resolvers; @@ -20,11 +21,15 @@ public sealed class LuceneQueryFieldTypeProvider : ISchemaBuilder private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; + internal readonly IStringLocalizer S; + public LuceneQueryFieldTypeProvider( IHttpContextAccessor httpContextAccessor, + IStringLocalizer stringLocalizer, ILogger logger) { _httpContextAccessor = httpContextAccessor; + S = stringLocalizer; _logger = logger; } @@ -87,7 +92,7 @@ public async Task BuildAsync(ISchema schema) } } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) { var properties = querySchema["properties"]?.AsObject(); if (properties == null) @@ -150,7 +155,7 @@ private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySc new QueryArgument { Name = "parameters" } ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = new ListGraphType(typeType), Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeof(ListGraphType>) @@ -176,7 +181,7 @@ async ValueTask ResolveAsync(IResolveFieldContext context) return fieldType; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + private FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) { var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); @@ -191,7 +196,7 @@ private static FieldType BuildContentTypeFieldType(ISchema schema, string conten new QueryArgument { Name = "parameters" } ), Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, + Description = S["Represents the {0} Query : {1}", query.Source, query.Name], ResolvedType = typeType.ResolvedType, Resolver = new LockedAsyncFieldResolver(ResolveAsync), Type = typeType.Type diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/MetaEntryQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/MetaEntryQueryObjectType.cs index 370cd1a442f..18b8068f12b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/MetaEntryQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/MetaEntryQueryObjectType.cs @@ -13,17 +13,22 @@ public MetaEntryQueryObjectType(IStringLocalizer S) Description = S["Meta entry"]; Field(x => x.Name) - .Description("Name of the meta entry"); + .Description(S["Name of the meta entry"]); + Field(x => x.Property) - .Description("Property of the meta entry"); + .Description(S["Property of the meta entry"]); + Field(x => x.Content) - .Description("Content of the meta entry"); + .Description(S["Content of the meta entry"]); + Field(x => x.HttpEquiv) - .Description("HttpEquiv of the meta entry"); + .Description(S["HttpEquiv of the meta entry"]); + Field(x => x.Charset) - .Description("Charset of the meta entry"); + .Description(S["Charset of the meta entry"]); + Field("Tag") - .Description("The generated tag of the meta entry") + .Description(S["The generated tag of the meta entry"]) .Resolve(ctx => { using var writer = new StringWriter(); diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/SeoMetaQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/SeoMetaQueryObjectType.cs index ebac25eff70..60a8f516631 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/SeoMetaQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/GraphQL/SeoMetaQueryObjectType.cs @@ -13,17 +13,22 @@ public SeoMetaQueryObjectType(IStringLocalizer S) Description = S["SEO meta fields"]; Field(x => x.Render) - .Description("Whether to render the seo metas"); + .Description(S["Whether to render the seo metas"]); + Field(x => x.PageTitle, true) - .Description("The seo page title"); + .Description(S["The seo page title"]); + Field(x => x.MetaDescription, true) - .Description("The meta description of the content item"); + .Description(S["The meta description of the content item"]); + Field(x => x.MetaKeywords, true) - .Description("The meta keywords of the content item"); + .Description(S["The meta keywords of the content item"]); + Field(x => x.Canonical, true) - .Description("The canonical link of the content item"); + .Description(S["The canonical link of the content item"]); + Field(x => x.MetaRobots, true) - .Description("The content item specific meta robots definition"); + .Description(S["The content item specific meta robots definition"]); Field>("customMetaTags") .Resolve(ctx => ctx.Source.CustomMetaTags); @@ -35,28 +40,33 @@ public SeoMetaQueryObjectType(IStringLocalizer S) .Resolve(ctx => ctx.Source.OpenGraphImage); Field(x => x.OpenGraphType, true) - .Description("The seo meta opengraph type"); + .Description(S["The seo meta opengraph type"]); + Field(x => x.OpenGraphTitle, true) - .Description("The seo meta opengraph title"); + .Description(S["The seo meta opengraph title"]); + Field(x => x.OpenGraphDescription, true) - .Description("The seo meta opengraph description"); + .Description(S["The seo meta opengraph description"]); Field>("twitterImage") .Resolve(ctx => ctx.Source.TwitterImage); Field(x => x.TwitterTitle, true) - .Description("The seo meta twitter title"); + .Description(S["The seo meta twitter title"]); + Field(x => x.TwitterDescription, true) - .Description("The seo meta twitter description"); + .Description(S["The seo meta twitter description"]); + Field(x => x.TwitterCard, true) - .Description("The seo meta twitter card"); + .Description(S["The seo meta twitter card"]); + Field(x => x.TwitterCreator, true) - .Description("The seo meta twitter creator"); + .Description(S["The seo meta twitter creator"]); + Field(x => x.TwitterSite, true) - .Description("The seo meta twitter site"); + .Description(S["The seo meta twitter site"]); Field(x => x.GoogleSchema, true) - .Description("The seo meta google schema"); - + .Description(S["The seo meta google schema"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs index 9d0a5cdb958..90d6a654622 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs @@ -1,6 +1,7 @@ using System.Text.Json.Nodes; using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL; using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.GraphQL.Queries.Types; @@ -10,12 +11,12 @@ namespace OrchardCore.Taxonomies.GraphQL; public class TaxonomyFieldQueryObjectType : ObjectGraphType { - public TaxonomyFieldQueryObjectType() + public TaxonomyFieldQueryObjectType(IStringLocalizer S) { Name = nameof(TaxonomyField); Field, IEnumerable>("termContentItemIds") - .Description("term content item ids") + .Description(S["term content item ids"]) .PagingArguments() .Resolve(x => { @@ -23,14 +24,14 @@ public TaxonomyFieldQueryObjectType() }); Field("taxonomyContentItemId") - .Description("taxonomy content item id") + .Description(S["taxonomy content item id"]) .Resolve(x => { return x.Source.TaxonomyContentItemId; }); Field, List>("termContentItems") - .Description("the term content items") + .Description(S["the term content items"]) .PagingArguments() .ResolveLockedAsync(async x => { @@ -59,7 +60,7 @@ public TaxonomyFieldQueryObjectType() }); Field("taxonomyContentItem") - .Description("the taxonomy content item") + .Description(S["the taxonomy content item"]) .ResolveLockedAsync(async context => { var contentManager = context.RequestServices.GetService(); diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs index 16f7692a9dd..95861d0b268 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs @@ -1,4 +1,5 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL; using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.GraphQL.Queries.Types; @@ -8,14 +9,14 @@ namespace OrchardCore.Taxonomies.GraphQL; public class TaxonomyPartQueryObjectType : ObjectGraphType { - public TaxonomyPartQueryObjectType() + public TaxonomyPartQueryObjectType(IStringLocalizer S) { Name = "TaxonomyPart"; Field(x => x.TermContentType); Field, IEnumerable>("contentItems") - .Description("the content items") + .Description(S["the content items"]) .PagingArguments() .Resolve(x => x.Page(x.Source.Terms)); } diff --git a/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Queries/WhereInputObjectGraphType.cs b/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Queries/WhereInputObjectGraphType.cs index c084f6af265..01e13bedea5 100644 --- a/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Queries/WhereInputObjectGraphType.cs +++ b/src/OrchardCore/OrchardCore.Apis.GraphQL.Abstractions/Queries/WhereInputObjectGraphType.cs @@ -1,14 +1,25 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL.Queries.Types; namespace OrchardCore.Apis.GraphQL.Queries; public class WhereInputObjectGraphType : WhereInputObjectGraphType, IFilterInputObjectGraphType { + public WhereInputObjectGraphType(IStringLocalizer> stringLocalizer) : base(stringLocalizer) + { + } } public class WhereInputObjectGraphType : InputObjectGraphType, IFilterInputObjectGraphType { + protected readonly IStringLocalizer S; + + public WhereInputObjectGraphType(IStringLocalizer> stringLocalizer) + { + S = stringLocalizer; + } + // arguments of typed input graph types return typed object, without additional input fields (_in, _contains,..) // so we return dictionary as it was before. public override object ParseDictionary(IDictionary value) @@ -17,37 +28,37 @@ public override object ParseDictionary(IDictionary value) } // Applies to all types. - public static readonly Dictionary EqualityOperators = new() + public static readonly Dictionary> EqualityOperators = new() { - { "", "is equal to" }, - { "_not", "is not equal to" }, + { "", (S, description) => S["{0} is equal to", description] }, + { "_not", (S, description) => S["{0} is not equal to", description] }, }; // Applies to all types. - public static readonly Dictionary MultiValueComparisonOperators = new() + public static readonly Dictionary> MultiValueComparisonOperators = new() { - { "_in", "is in collection" }, - { "_not_in", "is not in collection" }, + { "_in", (S, description) => S["{0} is in collection", description] }, + { "_not_in", (S, description) => S["{0} is not in collection", description] }, }; // Applies to non strings. - public static readonly Dictionary NonStringValueComparisonOperators = new() + public static readonly Dictionary> NonStringValueComparisonOperators = new() { - { "_gt", "is greater than" }, - { "_gte", "is greater than or equal" }, - { "_lt", "is less than" }, - { "_lte", "is less than or equal" }, + { "_gt", (S, description) => S["{0} is greater than", description] }, + { "_gte", (S, description) => S["{0} is greater than or equal", description] }, + { "_lt", (S, description) => S["{0} is less than", description] }, + { "_lte", (S, description) => S["{0} is less than or equal", description] }, }; // Applies to strings. - public static readonly Dictionary StringComparisonOperators = new() + public static readonly Dictionary> StringComparisonOperators = new() { - {"_contains", "contains the string"}, - {"_not_contains", "does not contain the string"}, - {"_starts_with", "starts with the string"}, - {"_not_starts_with", "does not start with the string"}, - {"_ends_with", "ends with the string"}, - {"_not_ends_with", "does not end with the string"}, + { "_contains", (S, description) => S["{0} contains the string", description] }, + { "_not_contains", (S, description) => S["{0} does not contain the string", description] }, + { "_starts_with", (S, description) => S["{0} starts with the string", description] }, + { "_not_starts_with", (S, description) => S["{0} does not start with the string", description] }, + { "_ends_with", (S, description) => S["{0} ends with the string", description] }, + { "_not_ends_with", (S, description) => S["{0} does not end with the string", description] }, }; public virtual void AddScalarFilterFields(string fieldName, string description) @@ -107,7 +118,7 @@ private void AddMultiValueFilters(Type graphType, string fieldName, string descr private void AddFilterFields( IGraphType resolvedType, - IDictionary filters, + IDictionary> filters, string fieldName, string description) { @@ -116,7 +127,7 @@ private void AddFilterFields( AddField(new FieldType { Name = fieldName + filter.Key, - Description = $"{description} {filter.Value}", + Description = filter.Value(S, description), ResolvedType = resolvedType, }); } diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemsFieldType.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemsFieldType.cs index 850c09bdd92..db61b340738 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemsFieldType.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemsFieldType.cs @@ -4,6 +4,7 @@ using GraphQL; using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Queries; @@ -22,57 +23,54 @@ namespace OrchardCore.ContentManagement.GraphQL.Queries; /// public class ContentItemsFieldType : FieldType { - private static readonly List _contentItemProperties; private readonly int _defaultNumberOfItems; - static ContentItemsFieldType() + public ContentItemsFieldType( + string contentItemName, + ISchema schema, + IOptions optionsAccessor, + IOptions settingsAccessor, + IServiceProvider serviceProvider) { - _contentItemProperties = []; + Name = contentItemName; - foreach (var property in typeof(ContentItemIndex).GetProperties()) - { - _contentItemProperties.Add(property.Name); - } - } + Type = typeof(ListGraphType); - public ContentItemsFieldType(string contentItemName, ISchema schema, IOptions optionsAccessor, IOptions settingsAccessor) - { - Name = "ContentItems"; + var S = serviceProvider.GetRequiredService>(); - Type = typeof(ListGraphType); - var whereInput = new ContentItemWhereInput(contentItemName, optionsAccessor); + var whereInput = new ContentItemWhereInput(contentItemName, optionsAccessor, serviceProvider.GetRequiredService>()); var orderByInput = new ContentItemOrderByInput(contentItemName); Arguments = new QueryArguments( new QueryArgument { Name = "where", - Description = "filters the content items", + Description = S["filters the content items"], ResolvedType = whereInput, }, new QueryArgument { Name = "orderBy", - Description = "sort order", + Description = S["sort order"], ResolvedType = orderByInput, }, new QueryArgument { Name = "first", - Description = "the first n content items", + Description = S["the first n content items"], ResolvedType = new IntGraphType(), }, new QueryArgument { Name = "skip", - Description = "the number of content items to skip", + Description = S["the number of content items to skip"], ResolvedType = new IntGraphType(), }, new QueryArgument { Name = "status", - Description = "publication status of the content item", - ResolvedType = new PublicationStatusGraphType(), + Description = S["publication status of the content item"], + ResolvedType = new PublicationStatusGraphType(serviceProvider.GetRequiredService>()), DefaultValue = PublicationStatusEnum.Published, } ); @@ -87,7 +85,6 @@ public ContentItemsFieldType(string contentItemName, ISchema schema, IOptions> ResolveAsync(IResolveFieldContext context) - { JsonObject where = null; if (context.HasArgument("where")) diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentTypeQuery.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentTypeQuery.cs index f99d9125630..8f75f24634c 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentTypeQuery.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentTypeQuery.cs @@ -54,15 +54,14 @@ public async Task BuildAsync(ISchema schema) continue; } - var typeType = new ContentItemType(_contentOptionsAccessor) + var typeType = new ContentItemType(_contentOptionsAccessor, serviceProvider.GetRequiredService>()) { Name = typeDefinition.Name, Description = S["Represents a {0}.", typeDefinition.DisplayName], }; - var query = new ContentItemsFieldType(typeDefinition.Name, schema, _contentOptionsAccessor, _settingsAccessor) + var query = new ContentItemsFieldType(typeDefinition.Name, schema, _contentOptionsAccessor, _settingsAccessor, serviceProvider) { - Name = typeDefinition.Name, Description = S["Represents a {0}.", typeDefinition.DisplayName], ResolvedType = new ListGraphType(typeType), }; diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/PublicationStatusGraphType.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/PublicationStatusGraphType.cs index de831b8fa23..dcb58576e62 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/PublicationStatusGraphType.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/PublicationStatusGraphType.cs @@ -1,16 +1,17 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; namespace OrchardCore.ContentManagement.GraphQL.Queries; public sealed class PublicationStatusGraphType : EnumerationGraphType { - public PublicationStatusGraphType() + public PublicationStatusGraphType(IStringLocalizer S) { Name = "Status"; - Description = "publication status"; - Add("PUBLISHED", PublicationStatusEnum.Published, "published content item version"); - Add("DRAFT", PublicationStatusEnum.Draft, "draft content item version"); - Add("LATEST", PublicationStatusEnum.Latest, "the latest version, either published or draft"); - Add("ALL", PublicationStatusEnum.All, "all historical versions"); + Description = S["publication status"]; + Add("PUBLISHED", PublicationStatusEnum.Published, S["published content item version"]); + Add("DRAFT", PublicationStatusEnum.Draft, S["draft content item version"]); + Add("LATEST", PublicationStatusEnum.Latest, S["the latest version, either published or draft"]); + Add("ALL", PublicationStatusEnum.All, S["all historical versions"]); } } diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemOrderByInput.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemOrderByInput.cs index e61cd619e24..49c7b45f036 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemOrderByInput.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemOrderByInput.cs @@ -1,4 +1,5 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; namespace OrchardCore.ContentManagement.GraphQL.Queries.Types; @@ -30,11 +31,11 @@ public enum OrderByDirection public class OrderByDirectionGraphType : EnumerationGraphType { - public OrderByDirectionGraphType() + public OrderByDirectionGraphType(IStringLocalizer S) { Name = "OrderByDirection"; - Description = "the order by direction"; - Add("ASC", OrderByDirection.Ascending, "orders content items in ascending order"); - Add("DESC", OrderByDirection.Descending, "orders content items in descending order"); + Description = S["the order by direction"]; + Add("ASC", OrderByDirection.Ascending, S["orders content items in ascending order"]); + Add("DESC", OrderByDirection.Descending, S["orders content items in descending order"]); } } diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemType.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemType.cs index 93e72619446..ff8a54b24a2 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemType.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemType.cs @@ -3,6 +3,7 @@ using GraphQL; using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using OrchardCore.Apis.GraphQL; using OrchardCore.ContentManagement.Display; @@ -16,47 +17,47 @@ public sealed class ContentItemType : ObjectGraphType { private readonly GraphQLContentOptions _options; - public ContentItemType(IOptions optionsAccessor) + public ContentItemType(IOptions optionsAccessor, IStringLocalizer S) { _options = optionsAccessor.Value; Name = "ContentItemType"; Field(ci => ci.ContentItemId) - .Description("Content item id"); + .Description(S["Content item id"]); Field(ci => ci.ContentItemVersionId, nullable: true) - .Description("The content item version id"); + .Description(S["The content item version id"]); Field(ci => ci.ContentType) - .Description("Type of content"); + .Description(S["Type of content"]); Field(ci => ci.DisplayText, nullable: true) - .Description("The display text of the content item"); + .Description(S["The display text of the content item"]); Field(ci => ci.Published) - .Description("Is the published version"); + .Description(S["Is the published version"]); Field(ci => ci.Latest) - .Description("Is the latest version"); + .Description(S["Is the latest version"]); Field("modifiedUtc") .Resolve(ci => ci.Source.ModifiedUtc) - .Description("The date and time of modification"); + .Description(S["The date and time of modification"]); Field("publishedUtc") .Resolve(ci => ci.Source.PublishedUtc) - .Description("The date and time of publication"); + .Description(S["The date and time of publication"]); Field("createdUtc") .Resolve(ci => ci.Source.CreatedUtc) - .Description("The date and time of creation"); + .Description(S["The date and time of creation"]); Field(ci => ci.Owner, nullable: true) - .Description("The owner of the content item"); + .Description(S["The owner of the content item"]); Field(ci => ci.Author) - .Description("The author of the content item"); + .Description(S["The author of the content item"]); Field("render") .ResolveLockedAsync(RenderShapeAsync); diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemWhereInput.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemWhereInput.cs index e3015d46d7d..2e9875c1363 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemWhereInput.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/ContentItemWhereInput.cs @@ -1,4 +1,5 @@ using GraphQL.Types; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using OrchardCore.Apis.GraphQL; using OrchardCore.Apis.GraphQL.Queries; @@ -13,27 +14,37 @@ public sealed class ContentItemWhereInput : WhereInputObjectGraphType { private readonly IOptions _optionsAccessor; - public ContentItemWhereInput(string contentItemName, IOptions optionsAccessor) + public ContentItemWhereInput(string contentItemName, IOptions optionsAccessor, IStringLocalizer stringLocalizer) + : base(stringLocalizer) { _optionsAccessor = optionsAccessor; Name = $"{contentItemName}WhereInput"; - Description = $"the {contentItemName} content item filters"; + Description = S["the {0} content item filters", contentItemName]; - AddScalarFilterFields("contentItemId", "content item id"); - AddScalarFilterFields("contentItemVersionId", "the content item version id"); - AddScalarFilterFields("displayText", "the display text of the content item"); - AddScalarFilterFields("createdUtc", "the date and time of creation"); - AddScalarFilterFields("modifiedUtc", "the date and time of modification"); - AddScalarFilterFields("publishedUtc", "the date and time of publication"); - AddScalarFilterFields("owner", "the owner of the content item"); - AddScalarFilterFields("author", "the author of the content item"); + AddScalarFilterFields("contentItemId", S["content item id"]); + AddScalarFilterFields("contentItemVersionId", S["the content item version id"]); + AddScalarFilterFields("displayText", S["the display text of the content item"]); + AddScalarFilterFields("createdUtc", S["the date and time of creation"]); + AddScalarFilterFields("modifiedUtc", S["the date and time of modification"]); + AddScalarFilterFields("publishedUtc", S["the date and time of publication"]); + AddScalarFilterFields("owner", S["the owner of the content item"]); + AddScalarFilterFields("author", S["the author of the content item"]); var whereInputType = new ListGraphType(this); - Field>("Or").Description("OR logical operation").Type(whereInputType); - Field>("And").Description("AND logical operation").Type(whereInputType); - Field>("Not").Description("NOT logical operation").Type(whereInputType); + + Field>("Or") + .Description(S["OR logical operation"]) + .Type(whereInputType); + + Field>("And") + .Description(S["AND logical operation"]) + .Type(whereInputType); + + Field>("Not") + .Description(S["NOT logical operation"]) + .Type(whereInputType); } public override void AddScalarFilterFields(Type graphType, string fieldName, string description) diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeBuilder.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeBuilder.cs index 026b8fa76e3..59e4ceb3092 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeBuilder.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeBuilder.cs @@ -172,7 +172,7 @@ protected void BuildInternal(ISchema schema, ContentTypeDefinition contentTypeDe Name = partFieldName, Description = S["Represents a {0}.", part.PartDefinition.Name], Type = typeof(DynamicPartWhereInputGraphType), - ResolvedType = new DynamicPartWhereInputGraphType(part) + ResolvedType = new DynamicPartWhereInputGraphType(part, serviceProvider.GetRequiredService>()) }; inputGraphType.AddField(field); diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeWhereInputBuilder.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeWhereInputBuilder.cs index c2fa5081c66..d69a2b7318c 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeWhereInputBuilder.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicContentTypeWhereInputBuilder.cs @@ -12,8 +12,8 @@ public sealed class DynamicContentTypeWhereInputBuilder : DynamicContentTypeBuil public DynamicContentTypeWhereInputBuilder( IHttpContextAccessor httpContextAccessor, IOptions contentOptionsAccessor, - IStringLocalizer localizer) - : base(httpContextAccessor, contentOptionsAccessor, localizer) { } + IStringLocalizer stringLocalizer) + : base(httpContextAccessor, contentOptionsAccessor, stringLocalizer) { } public override void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType) { diff --git a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicPartWhereInputGraphType.cs b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicPartWhereInputGraphType.cs index c10b108bad1..e56770895f0 100644 --- a/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicPartWhereInputGraphType.cs +++ b/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/Types/DynamicPartWhereInputGraphType.cs @@ -1,5 +1,6 @@ using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using OrchardCore.Apis.GraphQL.Queries; using OrchardCore.ContentManagement.Metadata.Models; @@ -9,7 +10,8 @@ public sealed class DynamicPartWhereInputGraphType : WhereInputObjectGraphType stringLocalizer) + : base(stringLocalizer) { Name = $"{part.Name}WhereInput"; _part = part; diff --git a/test/OrchardCore.Tests/Apis/GraphQL/ContentItemsFieldTypeTests.cs b/test/OrchardCore.Tests/Apis/GraphQL/ContentItemsFieldTypeTests.cs index 904d0a88885..a0da2bb775d 100644 --- a/test/OrchardCore.Tests/Apis/GraphQL/ContentItemsFieldTypeTests.cs +++ b/test/OrchardCore.Tests/Apis/GraphQL/ContentItemsFieldTypeTests.cs @@ -13,6 +13,7 @@ using OrchardCore.Environment.Shell; using OrchardCore.Extensions; using OrchardCore.Json; +using OrchardCore.Localization; using YesSql.Indexes; using YesSql.Provider.Sqlite; using YesSql.Serialization; @@ -143,6 +144,8 @@ public async Task ShouldFilterByContentItemIndex() services.Services.AddScoped(x => new ShellSettings()); services.Services.AddSingleton>(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Build(); var context = CreateAnimalFieldContext(services); @@ -154,7 +157,7 @@ public async Task ShouldFilterByContentItemIndex() await session.SaveAsync(ci); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(services), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(services), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"contentItemId\": \"1\" }"), ArgumentSource.Variable); var dogs = await ((LockedAsyncFieldResolver>)type.Resolver) @@ -175,6 +178,8 @@ public async Task ShouldFilterByContentItemIndexWhenSqlTablePrefixIsUsed() services.Services.AddSingleton>(); services.Services.AddSingleton>(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); var shellSettings = new ShellSettings(); shellSettings["TablePrefix"] = _prefix; @@ -191,7 +196,7 @@ public async Task ShouldFilterByContentItemIndexWhenSqlTablePrefixIsUsed() await session.SaveAsync(ci); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(services), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(services), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"contentItemId\": \"1\" }"), ArgumentSource.Variable); var dogs = await ResolveContentItems(type, context); @@ -215,6 +220,8 @@ public async Task ShouldFilterByAliasIndexRegardlessOfInputFieldCase(string fiel services.Services.AddIndexProvider(); services.Services.AddScoped(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Build(); var context = CreateAnimalFieldContext(services, fieldName); @@ -229,7 +236,8 @@ public async Task ShouldFilterByAliasIndexRegardlessOfInputFieldCase(string fiel var type = new ContentItemsFieldType("Animal", new Schema(services), Options.Create(new GraphQLContentOptions()), - Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), + context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse($"{{\"{fieldName}\" : {{ \"name\": \"doug\" }} }}"), ArgumentSource.Variable); var dogs = await ResolveContentItems(type, context); @@ -251,6 +259,8 @@ public async Task ShouldBeAbleToUseTheSameIndexForMultipleAliases() services.Services.AddIndexProvider(); services.Services.AddScoped(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Build(); var context = CreateAnimalFieldContext(services); @@ -262,7 +272,7 @@ public async Task ShouldBeAbleToUseTheSameIndexForMultipleAliases() await session.SaveAsync(ci); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"cats\": { \"name\": \"doug\" } }"), ArgumentSource.Variable); var cats = await ResolveContentItems(type, context); @@ -291,6 +301,8 @@ public async Task ShouldFilterOnMultipleIndexesOnSameAlias() services.Services.AddIndexProvider(); services.Services.AddIndexProvider(); services.Services.AddScoped(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Services.AddSingleton>(); services.Services.AddSingleton>(); @@ -313,7 +325,7 @@ public async Task ShouldFilterOnMultipleIndexesOnSameAlias() await session.SaveAsync(ci2); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"animals\": { \"name\": \"doug\", \"isScary\": true } }"), ArgumentSource.Variable); var animals = await ResolveContentItems(type, context); @@ -337,6 +349,8 @@ public async Task ShouldFilterPartsWithoutAPrefixWhenThePartHasNoPrefix() services.Services.AddIndexProvider(); services.Services.AddScoped(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Build(); var context = CreateAnimalFieldContext(services); @@ -348,7 +362,7 @@ public async Task ShouldFilterPartsWithoutAPrefixWhenThePartHasNoPrefix() await session.SaveAsync(ci); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"animal\": { \"name\": \"doug\" } }"), ArgumentSource.Variable); var dogs = await ResolveContentItems(type, context); @@ -370,6 +384,8 @@ public async Task ShouldFilterByCollapsedWhereInputForCollapsedParts() services.Services.AddIndexProvider(); services.Services.AddScoped(); services.Services.AddSingleton>(); + services.Services.AddLocalization(); + services.Services.AddSingleton(); services.Build(); var context = CreateAnimalFieldContext(services, collapsed: true); @@ -381,7 +397,7 @@ public async Task ShouldFilterByCollapsedWhereInputForCollapsedParts() await session.SaveAsync(ci); await session.SaveChangesAsync(); - var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 })); + var type = new ContentItemsFieldType("Animal", new Schema(), Options.Create(new GraphQLContentOptions()), Options.Create(new GraphQLSettings { DefaultNumberOfResults = 10 }), context.RequestServices); context.Arguments["where"] = new ArgumentValue(JObject.Parse("{ \"name\": \"doug\" }"), ArgumentSource.Variable); var dogs = await ResolveContentItems(type, context); @@ -394,17 +410,18 @@ private static async Task> ResolveContentItems(ContentI { return (await ((LockedAsyncFieldResolver>)type.Resolver).ResolveAsync(context)) as IEnumerable; } + private static ResolveFieldContext CreateAnimalFieldContext(IServiceProvider services, string fieldName = null, bool collapsed = false) { IGraphType where; if (!collapsed) { - where = new AnimalPartWhereInput(fieldName ?? "Animal"); + where = new AnimalPartWhereInput(fieldName ?? "Animal", MockStringLocalizer()); } else { - where = new AnimalPartCollapsedWhereInput(); + where = new AnimalPartCollapsedWhereInput(MockStringLocalizer()); } return new ResolveFieldContext @@ -431,11 +448,20 @@ private static ResolveFieldContext CreateAnimalFieldContext(IServiceProvider ser RequestServices = services }; } + + private static IStringLocalizer MockStringLocalizer() + { + var localizerMock = new Mock>(); + localizerMock.Setup(x => x[It.IsAny()]).Returns((string arg) => new LocalizedString(arg, arg)); + + return localizerMock.Object; + } } public class AnimalPartWhereInput : WhereInputObjectGraphType { - public AnimalPartWhereInput(string fieldName) + public AnimalPartWhereInput(string fieldName, IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "Test"; Description = "Foo"; @@ -452,7 +478,8 @@ public AnimalPartWhereInput(string fieldName) public class AnimalPartCollapsedWhereInput : WhereInputObjectGraphType { - public AnimalPartCollapsedWhereInput() + public AnimalPartCollapsedWhereInput(IStringLocalizer stringLocalizer) + : base(stringLocalizer) { Name = "Test"; Description = "Foo";