这是indexloc提供的服务,不要输入任何密码
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3f74dcc
Add support for parsing GraphQL's QueryRoot and Mutation operations i…
nozzlegear Aug 26, 2025
91a40c0
Write QueryRoot and Mutation operations to service classes
nozzlegear Sep 29, 2025
0787978
Add constructors for generated Graph services
nozzlegear Oct 3, 2025
87560f9
Add GraphQL query infrastructure and refactor parser for service gene…
nozzlegear Oct 23, 2025
d1627ae
Add GraphQueryBuilder base class, make generated services implement/u…
nozzlegear Oct 26, 2025
e3b21f6
Add GraphQueryBuilder.AddUnion using IGraphUnionCase as TQuery constr…
nozzlegear Oct 28, 2025
46921ae
WIP: Experimenting with AddUnion APIs
nozzlegear Oct 30, 2025
4c68892
Refactor GraphQL parser into modular components
nozzlegear Nov 4, 2025
1e7b57a
Generate QueryBuilder classes for all GraphQL types in Shopify schema…
nozzlegear Nov 8, 2025
849b2b0
Update tests to use GraphQL document in ParserContext
nozzlegear Nov 8, 2025
8642f12
Make base GraphQL node and connection types implement IGraphQLObject
nozzlegear Nov 9, 2025
b6315d7
Fully qualify generic type names can collide with System types
nozzlegear Nov 9, 2025
e596fa5
Register interface types in IParserContext with original type name
nozzlegear Nov 9, 2025
6b1dfc5
Generate QueryBuilder classes for QueryRoot and Mutation operations
nozzlegear Nov 11, 2025
1568b33
Write generated GraphQL types and query builders to separate directories
nozzlegear Nov 12, 2025
632a8f7
Allow adding field selections on operations returning a field type
nozzlegear Nov 14, 2025
2441a08
Remove unused reference to main ShopifySharp project
nozzlegear Nov 14, 2025
6e7b5ba
Always generate QueryRoot type
nozzlegear Nov 14, 2025
5b62ad1
Fix broken method name when adding query builder arguments
nozzlegear Nov 14, 2025
31bfc7d
Fix the graphql type name passed to the query builder
nozzlegear Nov 14, 2025
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
18 changes: 14 additions & 4 deletions ShopifySharp.GraphQL.Parser.CLI/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,29 @@ public async Task<int> ExecuteAsync(GenerateOptions options, CancellationToken c
return 1;
}

if (!Directory.Exists(options.Output))
Directory.CreateDirectory(options.Output);
if (!Directory.Exists(options.TypesPath))
Directory.CreateDirectory(options.TypesPath);

if (!Directory.Exists(options.QueryBuildersPath))
Directory.CreateDirectory(options.QueryBuildersPath);

var casingType = options.CasingType switch
{
"camel" => Casing.Camel,
_ => Casing.Pascal
};
var destination = FileSystemDestination.NewDirectory(options.Output);
var typesDestination = FileSystemDestination.NewDirectory(options.TypesPath);
var queryBuildersDestination = FileSystemDestination.NewDirectory(options.QueryBuildersPath);

var mem = (await File.ReadAllTextAsync(options.GraphqlFilePath, cancellationToken)).AsMemory();

await Parser.ParseAndWriteAsync(destination, casingType, options.Nullability == true, mem, CancellationToken.None);
await Parser.ParseAndWriteAsync(
typesDestination,
queryBuildersDestination,
casingType,
options.Nullability == true,
mem,
CancellationToken.None);

return 0;
}
Expand Down
7 changes: 5 additions & 2 deletions ShopifySharp.GraphQL.Parser.CLI/GenerateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ namespace ShopifySharp.GraphQL.Parser.CLI;
[Verb("parse", aliases: ["generate"], HelpText = "Reads a .graphql file and generates C# classes, enums and input types compatible with ShopifySharp.")]
public record GenerateOptions
{
[Option('o', "output", Required = true, HelpText = "Output directory for generated C# files")]
public required string Output { get; init; }
[Option('t', "types-dir", Required = true, HelpText = "Output directory for generated type files")]
public required string TypesPath { get; init; }

[Option('b', "builders-dir", Required = true, HelpText = "Output directory for generated QueryBuilder files")]
public required string QueryBuildersPath { get; init; }

[Value(0, MetaName = "<schema.graphql>", HelpText = "Path to the GraphQL schema file to convert. Both JSON and GraphQL are accepted.")]
public required string GraphqlFilePath { get; init; }
Expand Down
70 changes: 14 additions & 56 deletions ShopifySharp.GraphQL.Parser.Tests/DomainTests.fs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
module ShopifySharp.GraphQL.Parser.Tests.DomainTests

open System.Threading
open FakeItEasy
open Faqt
open Faqt.Operators
open GraphQLParser.Visitors
open Xunit
open ShopifySharp.GraphQL.Parser

type DomainTests() =
let schema = """
type TestType {
id: ID!
name: String
}
"""
let document = GraphQLParser.Parser.Parse(schema)

[<Theory>]
[<CombinatorialData>]
Expand All @@ -35,7 +41,7 @@ type DomainTests() =
member _.``ParserContext.CancellationToken should return correct token``() =
// Setup
let cancellationToken = CancellationToken()
let sut = ParserContext(Pascal, false, cancellationToken)
let sut = ParserContext(Pascal, false, document, cancellationToken)

// Act & Assert
%sut.CancellationToken.Should().Be(cancellationToken)
Expand All @@ -53,7 +59,7 @@ type DomainTests() =
| "camel" -> Camel
| _ -> failwithf $"Unhandled {nameof casingType} value \"{casingType}\""
let cancellationToken = CancellationToken.None
let sut = ParserContext(casing, false, cancellationToken)
let sut = ParserContext(casing, false, document, cancellationToken)

// Act & Assert
%sut.CasingType.Should().Be(casing)
Expand All @@ -65,7 +71,7 @@ type DomainTests() =
) =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, assumeNullability, cancellationToken)
let sut = ParserContext(Pascal, assumeNullability, document, cancellationToken)
let context = sut :> IParsedContext

// Act & Assert
Expand All @@ -75,7 +81,7 @@ type DomainTests() =
member _.``ParserContext SetVisitedType should add type to collection``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let sut = ParserContext(Pascal, false, document, cancellationToken)
let testClass = VisitedTypes.Class {
Name = "TestClass"
XmlSummary = [||]
Expand All @@ -95,59 +101,11 @@ type DomainTests() =
| VisitedTypes.Class c -> %c.Name.Should().Be("TestClass")
| _ -> failwith "Expected Class type"

[<Fact>]
member _.``ParserContext AddUnionRelationship should add relationship``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let context = sut :> IParsedContext
let unionName = "TestUnion"
let unionCases = [| "Case1"; "Case2" |]

// Act
sut.AddUnionRelationship unionName unionCases

// Assert
for unionCase in unionCases do
%context.TypeIsKnownUnionCase(unionCase).Should().BeTrue()

[<Fact>]
member _.``ParserContext TryFindUnionRelationship should return relationship when exists``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let context = sut :> IParsedContext
let unionName = "TestUnion"
let unionCase = "TestCase"

sut.AddUnionRelationship unionName [| unionCase |]

// Act
let result = context.TryFindUnionRelationship unionCase

// Assert
%result.Should().BeSome()
%result.Value.UnionTypeName.Should().Be(unionName)
%result.Value.UnionCaseName.Should().Be(unionCase)

[<Fact>]
member _.``ParserContext TryFindUnionRelationship should return None when not exists``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let context = sut :> IParsedContext

// Act
let result = context.TryFindUnionRelationship "NonExistentCase"

// Assert
%result.Should().BeNone()

[<Fact>]
member _.``ParserContext AddNamedType should add type``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let sut = ParserContext(Pascal, false, document, cancellationToken)
let context = sut :> IParsedContext
let namedType = NamedType.Class "TestClass"

Expand All @@ -161,7 +119,7 @@ type DomainTests() =
member _.``ParserContext IsNamedType should return false when type not added``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let sut = ParserContext(Pascal, false, document, cancellationToken)
let context = sut :> IParsedContext
let namedType = NamedType.Class "TestClass"

Expand All @@ -172,7 +130,7 @@ type DomainTests() =
member _.``ParserContext TypeIsKnownUnionCase should return false when case not added``() =
// Setup
let cancellationToken = CancellationToken.None
let sut = ParserContext(Pascal, false, cancellationToken)
let sut = ParserContext(Pascal, false, document, cancellationToken)
let context = sut :> IParsedContext

// Act & Assert
Expand Down
1 change: 1 addition & 0 deletions ShopifySharp.GraphQL.Parser.Tests/IntegrationTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type IntegrationTests() =
| VisitedTypes.Enum e -> e.Name
| VisitedTypes.InputObject io -> io.Name
| VisitedTypes.UnionType u -> u.Name
| Operation o -> o.Name
)

%typeNames.Should().Contain("INode")
Expand Down
2 changes: 2 additions & 0 deletions ShopifySharp.GraphQL.Parser.Tests/ParserTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type ParserTests() =
| VisitedTypes.Enum e -> e.Name
| VisitedTypes.InputObject io -> io.Name
| VisitedTypes.UnionType u -> u.Name
| Operation o -> o.Name
)
%typeNames.Should().Contain("User")
%typeNames.Should().Contain("UserStatus")
Expand Down Expand Up @@ -114,6 +115,7 @@ type ParserTests() =
| VisitedTypes.Enum e -> e.Name
| VisitedTypes.InputObject io -> io.Name
| VisitedTypes.UnionType u -> u.Name
| VisitedTypes.Operation o -> o.Name
)

%typeNames.Should().Contain("INode")
Expand Down
Loading