Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GraphQL support to UserPickerField #15389

Merged
merged 27 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f35d133
Add UserPickerField
Feb 22, 2024
cb7bdae
Merge branch 'main' into UserPickerField
Feb 22, 2024
d85bc64
fix query
hyzx86 Feb 22, 2024
0974367
Merge branch 'UserPickerField' of https://github.com/hyzx86/OrchardCo…
Feb 23, 2024
1418b06
update dependency
Feb 23, 2024
52cc64b
Merge branch 'main' into UserPickerField
hyzx86 Mar 17, 2024
cd0d788
Merge branch 'main' into UserPickerField
hyzx86 Mar 18, 2024
c2645ae
Merge branch 'main' into UserPickerField
hyzx86 Mar 19, 2024
da29f40
Merge branch 'main' into UserPickerField
hyzx86 Mar 21, 2024
6f3a722
Merge branch 'main' into UserPickerField
hyzx86 Apr 1, 2024
74a2ae2
Merge branch 'main' into UserPickerField
hyzx86 Apr 2, 2024
e18892e
Makes the GraphQL user type reusable.
gvkries Apr 8, 2024
e3cc0af
Merge branch 'main' into UserPickerField
hyzx86 Apr 8, 2024
6c7a4cd
merge pr 15691
hyzx86 Apr 9, 2024
a210ba0
update with pr 15691
hyzx86 Apr 9, 2024
ab50f42
Merge branch 'UserPickerField' of https://github.com/hyzx86/OrchardCo…
hyzx86 Apr 9, 2024
dbbeb8e
Merge branch 'main' into UserPickerField
hyzx86 Apr 9, 2024
92cddfe
Merge branch 'main' into UserPickerField
hyzx86 Apr 12, 2024
0ce1bae
Merge branch 'main' into UserPickerField
Piedone Apr 21, 2024
c949c59
Merge branch 'main' into UserPickerField
hyzx86 Apr 22, 2024
7b22445
Merge branch 'main' into UserPickerField
hishamco Apr 23, 2024
6adcd22
Update src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Type…
hyzx86 Apr 24, 2024
2d57d5d
Update src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Type…
hyzx86 Apr 24, 2024
7cb2e44
Merge branch 'main' into UserPickerField
hyzx86 Apr 24, 2024
10d3d04
fix code
Apr 24, 2024
3d6a459
Update src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Type…
hyzx86 Apr 24, 2024
5e360be
remove firstUser field
hyzx86 Apr 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace OrchardCore.ContentFields.GraphQL
{
[RequireFeatures("OrchardCore.Apis.GraphQL")]
[RequireFeatures("OrchardCore.Apis.GraphQL","OrchardCore.Users")]
hyzx86 marked this conversation as resolved.
Show resolved Hide resolved
public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
Expand All @@ -19,6 +19,7 @@ public override void ConfigureServices(IServiceCollection services)
services.AddObjectGraphType<LinkField, LinkFieldQueryObjectType>();
services.AddObjectGraphType<HtmlField, HtmlFieldQueryObjectType>();
services.AddObjectGraphType<ContentPickerField, ContentPickerFieldQueryObjectType>();
services.AddObjectGraphType<UserPickerField, UserPickerFieldQueryObjectType>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System.Collections.Generic;
using System.Linq;
using GraphQL;
using GraphQL.DataLoader;
using GraphQL.Types;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Apis.GraphQL;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentManagement;
using OrchardCore.Users.GraphQL;
using OrchardCore.Users.Indexes;
using OrchardCore.Users.Models;
using YesSql;
using YesSql.Services;

namespace OrchardCore.ContentFields.GraphQL
{
public class UserPickerFieldQueryObjectType : ObjectGraphType<UserPickerField>
{
public UserPickerFieldQueryObjectType(UserType userType)
{
Name = nameof(UserPickerField);

Field<ListGraphType<StringGraphType>, IEnumerable<string>>("userIds")
.Description("user ids")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all descriptions should be localizable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

.PagingArguments()
.Resolve(x =>
Piedone marked this conversation as resolved.
Show resolved Hide resolved
{
return x.Page(x.Source.UserIds);
});

Field<ListGraphType<UserType>, IEnumerable<User>>("users")
.Type(new ListGraphType(userType))
.Description("the user items")
.PagingArguments()
.ResolveAsync(x =>
{
var userLoader = GetOrAddUserProfileByIdDataLoader(x);
return userLoader.LoadAsync(x.Page(x.Source.UserIds)).Then(itemResultSet =>
{
return itemResultSet.SelectMany(x => x);
hyzx86 marked this conversation as resolved.
Show resolved Hide resolved
});
});
Field<UserType, User>("user")
hyzx86 marked this conversation as resolved.
Show resolved Hide resolved
.Type(userType)
.Description("the first user")
.ResolveAsync(x =>
{
var userLoader = GetOrAddUserProfileByIdDataLoader(x);
return userLoader.LoadAsync(x.Source.UserIds.FirstOrDefault()).Then(itemResultSet =>
{
return itemResultSet.FirstOrDefault();
});
});
}

public static IDataLoader<string, IEnumerable<User>> GetOrAddUserProfileByIdDataLoader<T>(IResolveFieldContext<T> context)
Piedone marked this conversation as resolved.
Show resolved Hide resolved
{
IDataLoaderContextAccessor requiredService = context.RequestServices.GetRequiredService<IDataLoaderContextAccessor>();
var session = context.RequestServices.GetService<ISession>();
return requiredService.Context.GetOrAddCollectionBatchLoader("GetOrAddUserByIds", async (IEnumerable<string> userIds) =>
{
if (userIds == null || !userIds.Any())
{
return null;
Piedone marked this conversation as resolved.
Show resolved Hide resolved
}
var users = await session.Query<User, UserIndex>(y => y.UserId.IsIn(userIds)).ListAsync();

return users.ToLookup((User k) => k.UserId, (User user) => user);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Shortcodes.Abstractions\OrchardCore.Shortcodes.Abstractions.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.ResourceManagement\OrchardCore.ResourceManagement.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Users.Core\OrchardCore.Users.Core.csproj" />
<ProjectReference Include="..\OrchardCore.Users\OrchardCore.Users.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public class Startup : StartupBase
public override void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISchemaBuilder, CurrentUserQuery>();
services.AddTransient<UserType>();
services.AddScoped<UserType>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure you get the right instance of the UserType in any circumstances? I think we should change the way the UserType is build, what I did within the CurrentUserQuery seems not enough anymore.

Copy link
Contributor Author

@hyzx86 hyzx86 Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they have the same hash code
image

}
}
36 changes: 20 additions & 16 deletions src/OrchardCore.Modules/OrchardCore.Users/GraphQL/UserType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using GraphQL.Types;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.GraphQL.Queries.Types;
using OrchardCore.ContentManagement.Metadata.Models;
using OrchardCore.Users.Models;
using OrchardCore.Users.Services;
Expand Down Expand Up @@ -30,27 +32,29 @@ public UserType(IStringLocalizer<UserType> localizer)
internal void AddField(ISchema schema, ContentTypeDefinition typeDefinition)
{
var contentItemType = schema.AdditionalTypeInstances.SingleOrDefault(t => t.Name == typeDefinition.Name);

if (contentItemType == null)
{
// This error would indicate that this graph type is build too early.
throw new InvalidOperationException("ContentTypeDefinition has not been registered in GraphQL");
}

var field = Field(typeDefinition.Name, contentItemType.GetType())
.Description(S["Custom user settings of {0}.", typeDefinition.DisplayName])
.ResolveAsync(static async context => {
// We don't want to create an empty content item if it does not exist.
if (context.Source is User user &&
user.Properties.ContainsKey(context.FieldDefinition.ResolvedType.Name))
{
var customUserSettingsService = context.RequestServices!.GetRequiredService<CustomUserSettingsService>();
var settingsType = await customUserSettingsService.GetSettingsTypeAsync(context.FieldDefinition.ResolvedType.Name);

return await customUserSettingsService.GetSettingsAsync(user, settingsType);
}

return null;
});
Field<ContentItemInterface, ContentItem>(typeDefinition.Name)
.Type(contentItemType)
.Description(S["Custom user settings of {0}.", typeDefinition.DisplayName])
.ResolveAsync(static async context =>
{
// We don't want to create an empty content item if it does not exist.
if (context.Source is User user &&
user.Properties.ContainsKey(context.FieldDefinition.ResolvedType.Name))
{
var customUserSettingsService = context.RequestServices!.GetRequiredService<CustomUserSettingsService>();
var settingsType = await customUserSettingsService.GetSettingsTypeAsync(context.FieldDefinition.ResolvedType.Name);

return await customUserSettingsService.GetSettingsAsync(user, settingsType);
}

return null;
});
}
}
Loading