-
-
Notifications
You must be signed in to change notification settings - Fork 330
Generate fluent GraphQL query builders by parsing Shopify's schema #1224
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
base: master
Are you sure you want to change the base?
Conversation
…nto VisitedTypes This extends the GraphQL parser to properly handle query and mutation operations as distinct types from regular object types. - Add QueryOrMutation and OperationArgument types to Domain.fs - Modify VisitedTypes to include QueryOrMutation case - Implement visitOperationDefinitions function in Visitor.fs - Update VisitObjectTypeDefinitionAsync to handle QueryRoot and Mutation objects type: feature scope: graphql-parser
Sanitize parameter names against list of potential dotnet keywords
…ration This commit adds infrastructure for building GraphQL queries via the services. It also restructures the parser to properly handle union types, return types and query/mutation operations. Changes: - Add core infrastructure classes (Query, QueryOptions, QueryStringBuilder, RequiredArgument) for programmatic GraphQL query construction - Create AstNodeMapper module to separate AST node mapping logic from visitor pattern - Refactor union type handling to recursively map union case nodes as VisitedTypes - Introduce ReturnType discriminated union to distinguish between field types and visited graph types in query/mutation return values - Extend IParsedContext with document access and node lookup capabilities - Add support for mapping QueryRoot and Mutation operations to QueryOrMutation types - Update Writer to generate service classes from query/mutation operations type: feature scope: graphql-parser
…se GraphQueryBuilder methods
…aint The existing GraphQueryBuilder.AddUnion methods can't be used because they constrain `TQuery` to be an inheritor of `T`. However, due to the way GraphQL unions work, and due to C#'s lack of native unions, this isn't feasible – ShopifySharp's GraphQL union cases do not inherit from their union type parent. Instead, there's an internal union type mapper for every union case that the union case is deserialized into. Long story short, the constraint didn't work with GraphQL union types because no union case inherits from its union type. type: feature scope: graphql
2d968df to
e6d1b0b
Compare
531d84c to
23484ac
Compare
|
Hi @nozzlegear, |
|
Hey @clement911, hope you're doing well! My goal with this is to add a strongly-typed fluent API for all of the generated GraphQL types, queries and mutations. Each class will have methods like Here's what it looks like in practice: var shopQueryBuilder = new ShopQueryBuilder();
var graphQuery = shopQueryBuilder.AddFieldName()
.AddFieldContactEmail()
.AddFieldCurrencyCode()
.AddFieldUrl()
.Build();Which will output this GraphQL string: A more complicated example with arguments, unions and nested objects might look something like this: var ordersQuery = new OrdersQueryBuilder();
ordersQuery.AddArgumentFirst(50)
.AddFieldPageInfo(i =>
{
i.AddFieldStartCursor()
.AddFieldEndCursor()
.AddFieldHasPreviousPage()
.AddFieldHasNextPage();
return i;
})
.AddFieldNodes(n =>
{
n.AddFieldId()
.AddFieldName()
.AddUnionPurchasingEntity((PurchasingCompany u) =>
{
u.AddFieldCompany(c =>
{
c.AddFieldName();
return c;
});
return u;
})
.AddUnionPurchasingEntity((Customer c) =>
{
c.AddFieldFirstName()
.AddFieldLastName();
return c;
})
return n;
})
.Build();Which should create this GraphQL string: I'm planning on adding a special case to the GraphService that will let us pass these querybuilders directly to the service, where it will handle the serialization of the graphql string/parameterization of arguments, etc. I'm also not sold on the names of the Let me know what you think! |
|
Oh I see. |
Extract query builder writing, utility functions, and reserved keywords into separate modules. type: refactor scope: graphql-parser
… document This commit extends the parser to generate QueryBuilder classes for all GraphQL classes, interfaces, union types, queries and mutations. This refactoring improves code modularity and lays the groundwork for more comprehensive code generation. - Move service writing logic from Writer.fs to QueryBuilderWriter.fs - Extend VisitedTypes union to include Operation case - Add tryMap function to AstNodeMapper for graceful type mapping type: feature scope: graphql-parser
The ParserContext constructor now requires a GraphQL document parameter. Also updated union type assertions to use the Cases property instead of Types. type: refactor scope: tests
Allows Connection, Edge, PageInfo, and Node types to be used with GraphQueryBuilder. These are core GraphQL/Relay specification types that need query builders generated for them. Without implementing IGraphQLObject, they don't satisfy the generic type constraint on GraphQueryBuilder<T>, causing compilation errors. type: fix scope: graphql
type: fix scope: generated-types
This fixes the issue where type names were not being reported as interfaces due to a mismatch between how interfaces were registered vs how they were looked up: - Registration: NamedType.Interface "IDisplayableError" (with "I" prefix) - Lookup: NamedType.Interface "DisplayableError" (without "I" prefix, from schema) This caused isNamedType to return false, preventing `mapFieldTypeToString` from adding the "I" prefix to field types. type: fix scope: parser, generated-types
The Visitor no longer processes QueryRoot and Mutation types itself. Instead, the QueryBuilderWriter now checks for the QueryRoot and Mutation types specifically when iterating through all definitions in the GraphDocument. Once found, it iterates over all of the field definitions on the two types and maps them to operation QueryBuilders. type: refactor scope: parser
This commit adds a new `--builders-dir` option to the CLI, and renames `--output` to `--types-dir`. The generated QueryBuilders are now written to the given `--builders-dir` directory, separating them them from the generated GraphQL classes/interfaces/union types. This commit also extracts the various filesystem functions (i.e. writeFileToPath, etc) to a new `FileSystem` module. type: feature scope: parser
This adds support for writing the `AddValue()` method to all Operation query builders for operations that return a field type instead of a visited type. type: feature scope: parser, sourcegen
type: refactor scope: dependencies
Some operations and types have the QueryRoot itself as one of their field types, so it needs to be represented as a class. type: fix scope: sourcegen, parser
The query builder argument method names were accidentally being sanitized against a list of C# and CLR keywords, which led to the argument names turning into things like "AddArgument @metaspace". They don't need to be sanitized at all, instead I meant to use the `toCasing` function. type: fix scope: sourcegen, parser
The type name string here was accidentally suffixed with "QueryBuilder", so the querybuilder for every type would think that the "foo" type was actually named "fooQueryBuilder". type: fix scope: sourcegen, parser
This script standardizes the default settings and output directories for the types and querybuilders generated by ShopifySharp.GraphQL.Parser.CLI. type: feature scope: dev, sourcegen
… a CLR return type This is a more explicit method name which should make it more obvious what the method is going to do. type: refactor scope: sourcegen, parser
type: chore scope: IDE
This commit makes the QueryOptions property used by the `Query` class publicly accessible, allowing us to access and pass down options from parent query to subquery generically (i.e. without casting `IQuery<T>` to `Query<T>`). type: refactor scope: graphql
This commit adds a new constructor that accepts an IQuery<T> instance directly, letting query builders be initialized with pre-configured query objects. type: feature scope: graphql, sourcegen, graphquerybuilder
Continuing the type naming standardization in commit 068f488, this refactors ArgumentsBuilderWriter and UnionsBuilderWriter to use the qualifiedPascalTypeName helper function for consistent type name generation. The function ensures all generated type names follow the same naming/formatting pattern. type: refactor scope: graphql, sourcegen, graphquerybuilder
This fixes an issue where one of Shopify's GraphQL types had a field named `Query`, which was causing the compiler to throw an error due to the property and the field having an identical name. type: refactor scope: graphql, sourcegen, graphquerybuilder
This commit refactors the QueryBuilderWriter to generate constructors that accept a name
parameter and create the Query instance internally using the new IQuery
constructor. This allows the generated builders to once again create
subqueries that use the field's property name (i.e.
`_query.AddField(new Query("fieldName"))`.
Also fixes typo in Unions property initialization (was "Unios");
type: refactor
scope: graphql, sourcegen, graphquerybuilder
… to query builders
This lets users instantiate a query builder without needing to look up
the operation name or type name first:
Old:
`var shopBuilder = new ShopQueryBuilder("shop");`
New:
`var shopBuilder = new ShopQueryBuilder();`
type: feature
scope: graphql
…query instantiation Changed query builder field methods from Func<T,T> to Action<T> pattern, requiring explicit instantiation and invocation of nested query builders. Updated internal field naming from property-style to private field convention (_query). - Use Action<T> instead of Func<T,T> for field builder methods - Instantiate nested query builders explicitly before invoking build action - Change Query property to readonly _query field for consistency - Remove redundant namespace qualification in generic type references - Fix yield! usage in async pipeline operations type: refactor scope: graphql
type: chore scope: dependencies
This change refactors how the union builders work and how the unions are
used in the builder API. Instead of having a `.Unions` builder on the
root query builder, unions are instead moved back into the `.Fields`
builder. In addition, the builder property is now named based on the GraphQL
field (matching the rest of the fields builders)
```cs
public sealed class FooFieldsBuilder
{
public FooFieldsBuilder Id() {};
public FooFieldsBuilder Bar(Action<BarUnionCasesBuilder> build) {};
}
public sealed class BarUnionCasesBuilder
{
public BarUnionCasesBuilder OnBaz(Action<BazQueryBuilder> build){};
public BarUnionCasesBuilder OnBat(Action<BatQueryBuilder> build){};
}
```
This should make the API slightly less verbose (no need to call
.Unions when invoking a union), but more importantly it fixes a bug
where union builders could be generated with duplicate method signatures
when multiple fields shared the same union type.
type: refactor
scope: graphql, sourcegen, querybuilders
type: refactor scope: dev
type: feature scope: graphql, querybuilder
type: feature scope: graphql
type: refactor scope: graphql-parser
This allows each argument builder to have common `.AddArgument(s)` methods without generating them on each class. type: refactor scope: graphql-parser
Fields builders now inherit from FieldsBuilderBase instead of managing their own query field. Also updated the union case field handling to use an `AddUnionCase` method which merges union case queries into the parent select list. type: refactor scope: graphql-parser
Fixed the UnionsBuilderWriter to look up and pass the actual union type instead of the parent type when creating UnionCasesBuilderWriter instances. Previously it was passing the parent type to the cases writer, which caused the cases writer to write the wrong IQuery generic type and thus cause compilation errors. type: fix scope: graphql-parser
Fixed `TryFindGraphObjectType` comparison by using the Name property directly instead of converting the entire VisitedTypes object to string. type: fix scope: graphql-parser
Once merged, this PR will add a new GraphQueryBuilder base class, along with the generated query builders of all Shopify GraphQL types, union types, queries and mutations.
This will resolve #1137 and #1132.