Skip to content

Commit

Permalink
Fix GraphQL schema localization. (#17041)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Mike Alhayek <[email protected]>
  • Loading branch information
gvkries and MikeAlhayek authored Nov 26, 2024
1 parent 1061902 commit 2c1a310
Show file tree
Hide file tree
Showing 37 changed files with 333 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace OrchardCore.Alias.GraphQL;

public class AliasInputObjectType : WhereInputObjectGraphType<AliasPart>
{
public AliasInputObjectType(IStringLocalizer<AliasInputObjectType> S)
public AliasInputObjectType(IStringLocalizer<AliasInputObjectType> stringLocalizer)
: base(stringLocalizer)
{
Name = "AliasPartInput";
Description = S["the alias part of the content item"];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Globalization;
using GraphQL;
using GraphQL.MicrosoftDI;
using GraphQL.Types;
Expand All @@ -13,8 +14,7 @@ public sealed class SchemaService : ISchemaFactory
private readonly IServiceProvider _serviceProvider;
private readonly SemaphoreSlim _schemaGenerationSemaphore = new(1, 1);
private readonly ConcurrentDictionary<ISchemaBuilder, string> _identifiers = new();

private ISchema _schema;
private readonly ConcurrentDictionary<CultureInfo, ISchema> _schemas = new();

public SchemaService(
IEnumerable<ISchemaBuilder> schemaBuilders,
Expand All @@ -27,6 +27,7 @@ public SchemaService(
public async Task<ISchema> GetSchemaAsync()
{
var hasChanged = false;
var culture = CultureInfo.CurrentUICulture;

foreach (var builder in _schemaBuilders)
{
Expand All @@ -37,9 +38,9 @@ public async Task<ISchema> GetSchemaAsync()
}
}

if (_schema is object && !hasChanged)
if (!hasChanged && _schemas.TryGetValue(culture, out var existingSchema))
{
return _schema;
return existingSchema;
}

await _schemaGenerationSemaphore.WaitAsync();
Expand All @@ -55,9 +56,9 @@ public async Task<ISchema> GetSchemaAsync()
}
}

if (_schema is object && !hasChanged)
if (!hasChanged && _schemas.TryGetValue(culture, out existingSchema))
{
return _schema;
return existingSchema;
}

var serviceProvider = ShellScope.Services;
Expand Down Expand Up @@ -115,7 +116,7 @@ public async Task<ISchema> GetSchemaAsync()

schema.Initialize();

return _schema = schema;
return _schemas[culture] = schema;
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@using System.Globalization
<script asp-src="~/OrchardCore.Apis.GraphQL/Scripts/graphiql-orchard.js" at="Foot"></script>

<style>
Expand Down Expand Up @@ -63,6 +64,6 @@
}
</style>

<div id="graphiql" data-introspection-url="@Href("~/api/graphql")">
<div id="graphiql" data-introspection-url="@Href($"~/api/graphql?culture={CultureInfo.CurrentUICulture.Name}")">
<div class="loader"></div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace OrchardCore.Autoroute.GraphQL;

public class AutorouteInputObjectType : WhereInputObjectGraphType<AutoroutePart>
{
public AutorouteInputObjectType(IStringLocalizer<AutorouteInputObjectType> S)
public AutorouteInputObjectType(IStringLocalizer<AutorouteInputObjectType> stringLocalizer)
: base(stringLocalizer)
{
Name = "AutoroutePartInput";
Description = S["the custom URL part of the content item"];
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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),
Expand All @@ -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<StringGraphType>),
ResolvedType = new ListGraphType(new StringGraphType()),
UnderlyingType = typeof(MultiTextField),
Expand All @@ -105,6 +106,13 @@ public class ContentFieldsProvider : IContentFieldProvider
}
}.ToFrozenDictionary();

protected readonly IStringLocalizer S;

public ContentFieldsProvider(IStringLocalizer<ContentFieldsProvider> stringLocalizer)
{
S = stringLocalizer;
}

public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName)
{
if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldDescriptor))
Expand All @@ -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<ContentElement, object>(context =>
Expand Down Expand Up @@ -161,7 +169,7 @@ public bool HasFieldIndex(ContentPartFieldDefinition field)

private sealed class FieldTypeDescriptor
{
public string Description { get; set; }
public Func<IStringLocalizer, string> Description { get; set; }

public Type FieldType { get; set; }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using GraphQL;
using GraphQL.Types;
using Microsoft.Extensions.Localization;
using OrchardCore.Apis.GraphQL;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentManagement;
Expand All @@ -10,20 +11,20 @@ namespace OrchardCore.ContentFields.GraphQL;

public class ContentPickerFieldQueryObjectType : ObjectGraphType<ContentPickerField>
{
public ContentPickerFieldQueryObjectType()
public ContentPickerFieldQueryObjectType(IStringLocalizer<ContentPickerFieldQueryObjectType> S)
{
Name = nameof(ContentPickerField);

Field<ListGraphType<StringGraphType>, IEnumerable<string>>("contentItemIds")
.Description("content item ids")
.Description(S["content item ids"])
.PagingArguments()
.Resolve(x =>
{
return x.Page(x.Source.ContentItemIds);
});

Field<ListGraphType<ContentItemInterface>, IEnumerable<ContentItem>>("contentItems")
.Description("the content items")
.Description(S["the content items"])
.PagingArguments()
.ResolveAsync(x =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
using GraphQL.Types;
using Microsoft.Extensions.Localization;
using OrchardCore.ContentFields.Fields;

namespace OrchardCore.ContentFields.GraphQL.Types;

public class LinkFieldQueryObjectType : ObjectGraphType<LinkField>
{
public LinkFieldQueryObjectType()
public LinkFieldQueryObjectType(IStringLocalizer<LinkFieldQueryObjectType> 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"]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace OrchardCore.ContentLocalization.GraphQL;

public class LocalizationInputObjectType : WhereInputObjectGraphType<LocalizationPart>
{
public LocalizationInputObjectType(IStringLocalizer<LocalizationInputObjectType> S)
public LocalizationInputObjectType(IStringLocalizer<LocalizationInputObjectType> stringLocalizer)
: base(stringLocalizer)
{
Name = "LocalizationInputObjectType";
Description = S["the localization part of the content item"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public BagPartQueryObjectType(IStringLocalizer<BagPartQueryObjectType> S)
Description = S["A BagPart allows to add content items directly within another content item"];

Field<ListGraphType<ContentItemInterface>, IEnumerable<ContentItem>>("contentItems")
.Description("the content items")
.Description(S["the content items"])
.PagingArguments()
.Resolve(x => x.Page(x.Source.ContentItems));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
using GraphQL.Types;
using Microsoft.Extensions.Localization;

namespace OrchardCore.Flows.GraphQL;

public class FlowAlignmentEnum : EnumerationGraphType
{
public FlowAlignmentEnum()
public FlowAlignmentEnum(IStringLocalizer<FlowAlignmentEnum> 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."]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public FlowPartQueryObjectType(IStringLocalizer<FlowPartQueryObjectType> S)
Description = S["A FlowPart allows to add content items directly within another content item"];

Field<ListGraphType<ContentItemInterface>>("widgets")
.Description("The widgets.")
.Description(S["The widgets."])
.Resolve(context => context.Source.Widgets);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,23 +15,23 @@ namespace OrchardCore.Layers.GraphQL;

public class LayerQueryObjectType : ObjectGraphType<Layer>
{
public LayerQueryObjectType()
public LayerQueryObjectType(IStringLocalizer<LayerQueryObjectType> S)
{
Name = "Layer";

Field(layer => layer.Name)
.Description("The name of the layer.");
.Description(S["The name of the layer."]);

Field<ListGraphType<StringGraphType>, IEnumerable<Condition>>("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<ListGraphType<LayerWidgetQueryObjectType>, IEnumerable<ContentItem>>("widgets")
.Description("The widgets for this layer.")
.Argument<PublicationStatusGraphType>("status", "publication status of the widgets")
.Description(S["The widgets for this layer."])
.Argument<PublicationStatusGraphType>("status", S["publication status of the widgets"])
.ResolveLockedAsync(GetWidgetsForLayerAsync);

async ValueTask<IEnumerable<ContentItem>> GetWidgetsForLayerAsync(IResolveFieldContext<Layer> context)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using GraphQL.Types;
using Microsoft.Extensions.Localization;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.GraphQL.Queries.Types;
using OrchardCore.Layers.Models;
Expand All @@ -7,24 +8,24 @@ namespace OrchardCore.Layers.GraphQL;

public class LayerWidgetQueryObjectType : ObjectGraphType<ContentItem>
{
public LayerWidgetQueryObjectType()
public LayerWidgetQueryObjectType(IStringLocalizer<LayerWidgetQueryObjectType> S)
{
Name = "LayerWidget";

Field<StringGraphType>("zone")
.Description("The name of the widget's zone.")
.Description(S["The name of the widget's zone."])
.Resolve(context => context.Source.As<LayerMetadata>()?.Zone);

Field<DecimalGraphType>("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<LayerMetadata>()?.Position);

Field<BooleanGraphType>("renderTitle")
.Description("Whether to render the widget's title.")
.Description(S["Whether to render the widget's title."])
.Resolve(context => context.Source.As<LayerMetadata>()?.RenderTitle);

Field<ContentItemInterface>("widget")
.Description("A widget on this layer.")
.Description(S["A widget on this layer."])
.Resolve(context => context.Source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace OrchardCore.Lists.GraphQL;

public class ContainedInputObjectType : WhereInputObjectGraphType<ContainedPart>
{
public ContainedInputObjectType(IStringLocalizer<ContainedPart> S)
public ContainedInputObjectType(IStringLocalizer<ContainedInputObjectType> stringLocalizer)
: base(stringLocalizer)
{
Name = "ContainedPartInput";
Description = S["the list part of the content item"];
Expand Down
Loading

0 comments on commit 2c1a310

Please sign in to comment.