OrchardCMS / OrchardCore

Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
https://orchardcore.net
BSD 3-Clause "New" or "Revised" License
7.35k stars 2.37k forks source link

[GraphQL] Subscriptions support #4489

Open carlwoodhouse opened 4 years ago

carlwoodhouse commented 4 years ago

Supporting subscriptions would allow us to notify consumers of the api that content has changed; which they might use for invalidating caching, rebuilding gatsby sites, etc.

more info:

MatthijsKrempel commented 4 years ago

We have a simular request from the organisation, any pointers to how we should proceed?

sebastienros commented 4 years ago

You might be able to add a subscription already

Start with something like this services.AddSingleton<ISchemaBuilder, ContentItemSubscription>(); which will give you access to the ISchema that contains access to queries, mutations and subscriptions. From that just follow the documentation from GraphQL.NET

carlwoodhouse commented 4 years ago

Yeah just follow what’s in the links; I think the only thing is you have to enable web sockets .. plenty of examples about

On Thu, 7 Nov 2019 at 19:07, Sébastien Ros notifications@github.com wrote:

You might be able to add a subscription already

Start with something like this services.AddSingleton<ISchemaBuilder, ContentItemSubscription>(); which will give you access to the ISchema that contains access to queries, mutations and subscriptions. From that just follow the documentation from GraphQL.NET

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/OrchardCMS/OrchardCore/issues/4489?email_source=notifications&email_token=AADRUEB22XBYV36E2FYIEKLQSRRQHA5CNFSM4I6COKZ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDNPEMY#issuecomment-551219763, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADRUEDTGUXV6SL5ERPI7KTQSRRQHANCNFSM4I6COKZQ .

MatthijsKrempel commented 4 years ago

Ok, not quite familiar with it, but I will have a look. I don't think it is as simple as adding a schema, we also have to have the middleware handling the request.

jrestall commented 4 years ago

The sample link @carlwoodhouse provided is really good https://github.com/graphql-dotnet/server/blob/develop/samples/Samples.Server/Startup.cs

I would suggest implementing this under its own feature such as "OrchardCore.Apis.GraphQL.Subscriptions" so that WebSockets is only enabled when subscriptions are used. So you would call AddWebSockets/UseWebSockets from a SubscriptionsStartup class like how the Users module does it here.

As for what the schema would look like for subscriptions, we need to discuss. Here's some initial options:

type Subscription {
  contentItem(where: SubscriptionWhereInput) : ContentItemSubscriptionPayload

  # OPTION 1
  blogPost(where: BlogPostSubscriptionWhereInput): BlogPostSubscriptionPayload

  # OPTION 2
  blogPost(where: SubscriptionWhereInput): BlogPostSubscriptionPayload

  # OPTION 3
  blogPostCreated : BlogPost
  blogPostUpdated: BlogPost
  blogPostRemoved: BlogPost
  blogPostPublished: BlogPost
...
}

# FILTER OPTION 1 (Simpler)
input SubscriptionWhereInput {
  # Filter for a specific mutation:
  # CREATED, UPDATED, REMOVED, PUBLISHED, UNPUBLISHED, CLONED
  mutation_in: [MutationType!]
}

# FILTER OPTION 2 (Allows for flexibility in future to filter on BlogPost specific fields.)
input BlogPostSubscriptionWhereInput {
  # Filter for a specific mutation:
  # CREATED, UPDATED, REMOVED, PUBLISHED, UNPUBLISHED, CLONED
  mutation_in: [MutationType!]
}

type BlogPostSubscriptionPayload {
  mutation: MutationType!
  node: BlogPost
}

Then create a ISchemaBuilder like public class ContentTypeSubscriptions : ISchemaBuilder similar to ContentTypeQuery that uses IObservable and ContentHandlerBase. Not sure exactly how this part would work currently.

We would actually be the first headless CMS to support graphql subscriptions once completed which would be cool! 🎉

carlwoodhouse commented 4 years ago

Totally agree it should be feature based due to the sockets dependency

On Fri, 8 Nov 2019 at 14:09, James Restall notifications@github.com wrote:

The sample link @carlwoodhouse https://github.com/carlwoodhouse provided is really good https://github.com/graphql-dotnet/server/blob/develop/samples/Samples.Server/Startup.cs

I would suggest implementing this under its own feature such as "OrchardCore.Apis.GraphQL.Subscriptions" so that WebSockets is only enabled when subscriptions are used. So you would call AddWebSockets/UseWebSockets from a SubscriptionsStartup class like how the Users module does it here https://github.com/OrchardCMS/OrchardCore/blob/dev/src/OrchardCore.Modules/OrchardCore.Users/Startup.cs .

As for what the schema would look like for subscriptions, we need to discuss. Here's some initial options:

type Subscription {

contentItem(where: SubscriptionWhereInput) : ContentItemSubscriptionPayload

OPTION 1

blogPost(where: BlogPostSubscriptionWhereInput): BlogPostSubscriptionPayload

OPTION 2

blogPost(where: SubscriptionWhereInput): BlogPostSubscriptionPayload

OPTION 3

blogPostCreated : BlogPost

blogPostUpdated: BlogPost

blogPostRemoved: BlogPost

blogPostPublished: BlogPost

...

}

FILTER OPTION 1 (Simpler)

input SubscriptionWhereInput {

Filter for a specific mutation:

CREATED, UPDATED, REMOVED, PUBLISHED, UNPUBLISHED, CLONED

mutation_in: [MutationType!]

}

FILTER OPTION 2 (Allows for flexibility in future to filter on BlogPost specific fields.)

input BlogPostSubscriptionWhereInput {

Filter for a specific mutation:

CREATED, UPDATED, REMOVED, PUBLISHED, UNPUBLISHED, CLONED

mutation_in: [MutationType!]

}

type BlogPostSubscriptionPayload {

mutation: MutationType!

node: BlogPost

}

Then create a ISchemaBuilder like public class ContentTypeSubscriptions : ISchemaBuilder similar to ContentTypeQuery that uses IObservable and ContentHandlerBase. Not sure exactly how this part would work currently.

We would actually be the first headless CMS to support graphql subscriptions once completed which would be cool! 🎉

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/OrchardCMS/OrchardCore/issues/4489?email_source=notifications&email_token=AADRUEHFSFUJYWRRR6LMI4TQSVXJPA5CNFSM4I6COKZ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEDSG4HQ#issuecomment-551841310, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADRUEDNH37VYGWKODZZNOLQSVXJPANCNFSM4I6COKZQ .

sebastienros commented 4 years ago

I think you can only provide subs for

Because a query (sql or lucene) would be too complex to handle.

I'd like to hear about scenarios for subscriptions, and see how these points would solve them.

rserj commented 4 years ago

I'd like to hear about scenarios for subscriptions

Since Orchard Core an Application framework rather than just a CMS. Because of its multitenancy support, modularity, and great rendering engine, we have plans to use Orchard Core as FrontBack as a middleman between React client app and other backend services. We have non-CMS related scenarios where we need to send notifications from backend services to client React app, we use to use SignalR in the first Orchard for this purpose.

rserj commented 4 years ago

I dig a little bit further. The problem is: Graphql-dotnet still relies on Single instance Schema. But we use ISchemaFactory.GetSchemaAsync() we need it because if schema gets changed we can rebuild one plus each module can register their own ISchemaBuilder to modify the schema. We had such problem with single-instance schema before, please see https://github.com/OrchardCMS/OrchardCore/issues/2164 https://github.com/graphql-dotnet/server/issues/107 It is still a problem with Subscriptions, their approach doesn't fit for ours, so we have to implement and support some services

These are places in Startup.cs where a single schema is used: https://github.com/graphql-dotnet/server/blob/develop/samples/Samples.Server/Startup.cs

image image

I see multiple ways to adopt current implementation to our's 1) We have to reimplement the next services to use our's factory approach for Subscriptions

2) Try to embrace it by using a hack by registration services in our Startup.cs like

 services.AddTransient(typeof(IGraphQLExecuter<Schema>), provider =>
                    new DefaultGraphQLExecuter<Schema>(
                            provider.GetService<ISchemaFactory>().GetSchemaAsync().Result as Schema, 
                            provider.GetService<IDocumentExecuter>(),
                            provider.GetService<IOptions<GraphQLOptions>>(),
                            provider.GetServices<IDocumentExecutionListener>(),
                            provider.GetServices<IValidationRule>()));

3) Another way to create a Decorator and register one as Single Instance schema like

public class SchemaDecorator : Schema
    {
        public SchemaDecorator(IServiceProvider serviceProvider)
        {
            var dependencyResolver = serviceProvider.GetService<IDependencyResolver>();
            var factory = dependencyResolver.Resolve<ISchemaFactory>();
            var schema = factory.GetSchema().Result;
            .....// continue replace fields from our factory

and use it like in https://github.com/graphql-dotnet/server/blob/develop/samples/Samples.Server/Startup.cs instead of ChatSchema use SchemaDecorator

4) We can contact to graphql-dotnet dev and ask for solution/fix

5) Switch to Hot Chocolate https://hotchocolate.io here is comparisons https://www.reddit.com/r/graphql/comments/dpnqdx/hotchocolate_vs_graphqlnet/ https://github.com/ChilliCream/hotchocolate/issues/392 performance comparison: https://chillicream.com/blog/2019/06/05/hot-chocolate-9.0.0#performance

cc: @sebastienros

sebastienros commented 4 years ago

Are you sure hotchocolate would solve the issue?

carlwoodhouse commented 4 years ago

I’d love to switch to hot chocolate; I’ve been using it away from ocore and it’s a much better library; data loaders, Di support etc out of the box and tons of other features. the perf is much better too.

However ..

Can’t say if it would fix this or not; as didn’t look into it.

The upgrade to ocore would be massive. I know this cos I started on a poc branch several months ago.

It would be a major breaking change to anyone who has written their own graph types etc. So would need serious thought.

On Thu, 13 Aug 2020 at 18:30, Sébastien Ros notifications@github.com wrote:

Are you sure hotchocolate would solve the issue?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/OrchardCMS/OrchardCore/issues/4489#issuecomment-673608916, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADRUEE4L7CDLRQ6V4Y2SCTSAQPMLANCNFSM4I6COKZQ .

michaelstaib commented 4 years ago

The upcoming version 11 solves your issues, as I understand.

https://chillicream.github.io/hotchocolate/blog/2020/07/16/version-11

The new schema configuration API allows full async creation and update of dynamic schemas since Hot Chocolate supports schema stitching we reworked version 11 to be able to push schema changes to the GraphQL Gateway dynamically. Schema stitching/schema federation and standard schemas become the same and let you fuse multiple sources and update them on the fly.

If you guys need help, let us now.

And sorry for barging in on this issue :) I just saw it linked to another issue on our project.

sebastienros commented 4 years ago

At that point I don't expect many have implemented custom graphql extensions. Breaking would be fine, as long as we document how to upgrade the code.

We'd need to check we can still provide the support we have done, with parts, where clauses, paging, ...

rserj commented 4 years ago

Unfortunately, I do not have time to implement Hot Chocolate for the whole Orchard. But I implemented my own module based on the same abstractions we have, it was easy. I do not have to implement my own Middleware like we did with graphql-dotnet and rewriting SchemaFactory was pretty straightforward. I didn't integrate OrchardCore security model with hot chocolate but there are lots of integration points where we can do it. Plus there are already build in solutions for: -Paging https://hotchocolate.io/docs/pagination -Filtration and Sorting https://hotchocolate.io/docs/filters they were made via hot chocolate's Middleware https://hotchocolate.io/docs/middleware, which we can always rewrite or extend

I was thinking about using Schema stitching, where each OrchardCore module/feature can define their own schema, and it will be up to the module/feature to decide either combine their schema with default single schema or expose module's schema as a separate end-point. With Hot chocolate 11, it will be even easier to define schema right in the startup with a fluent builder api, please see https://chillicream.github.io/hotchocolate/blog/2020/07/16/version-11

I can share my implementation with someone who wishes to start implementing Hot chocolate