Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions GraphQLSourceGen.Samples/GraphQLSourceGen.Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@

<ItemGroup>
<AdditionalFiles Include="**\*.graphql" />
<None Include="schema-definition.graphql" CopyToOutputDirectory="PreserveNewest" />
<None Include="schema.graphql" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<!-- Custom configuration -->
<PropertyGroup>
<GraphQLSourceGenNamespace>GraphQL.Generated</GraphQLSourceGenNamespace>
<GraphQLSourceGenUseRecords>true</GraphQLSourceGenUseRecords>
<GraphQLSourceGenUseSchemaForTypeInference>true</GraphQLSourceGenUseSchemaForTypeInference>
<GraphQLSourceGenSchemaFiles>schema-definition.graphql</GraphQLSourceGenSchemaFiles>
<GraphQLSourceGenCustomScalarMappings>DateTime:System.DateTime;Date:System.DateOnly;Time:System.TimeOnly</GraphQLSourceGenCustomScalarMappings>
</PropertyGroup>

</Project>
13 changes: 13 additions & 0 deletions GraphQLSourceGen.Samples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ static void Main(string[] args)
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
}

// Demonstrate schema-aware fragment generation
try
{
// Create a SchemaAwareExample instance
var schemaAwareExample = new SchemaAwareExample();
SchemaAwareExample.Run();
}
catch (Exception ex)
{
Console.WriteLine($"Error in schema-aware example: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
}

Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
Expand Down
216 changes: 216 additions & 0 deletions GraphQLSourceGen.Samples/SchemaAwareExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using GraphQL.Generated;
using GraphQLSourceGen.Models;
using GraphQLSourceGen.Parsing;
using System;
using System.Collections.Generic;
using System.IO;

namespace GraphQLSourceGen.Samples
{
/// <summary>
/// Example demonstrating the schema-aware fragment generation
/// </summary>
public class SchemaAwareExample
{
public static void Run()
{
Console.WriteLine("\nSchema-Aware Fragment Generation Example");
Console.WriteLine("=========================================");

// Step 1: Load the GraphQL schema
string schemaFilePath = "schema-definition.graphql";

// Try different paths if the file is not found
if (!File.Exists(schemaFilePath))
{
// Try with full path from current directory
string currentDir = Directory.GetCurrentDirectory();
Console.WriteLine($"Current directory: {currentDir}");

// Try alternative paths
string[] possiblePaths = new[]
{
Path.Combine(currentDir, "schema-definition.graphql"),
Path.Combine(currentDir, "..", "..", "..", "schema-definition.graphql"),
Path.Combine(currentDir, "..", "..", "..", "..", "GraphQLSourceGen.Samples", "schema-definition.graphql")
};

foreach (string path in possiblePaths)
{
Console.WriteLine($"Trying path: {path}");
if (File.Exists(path))
{
schemaFilePath = path;
Console.WriteLine($"Found schema file at: {schemaFilePath}");
break;
}
}
}

if (!File.Exists(schemaFilePath))
{
Console.WriteLine($"Error: Could not find schema file at {schemaFilePath}");
Console.WriteLine("Please ensure the schema-definition.graphql file is in the correct location.");
return;
}

string schemaContent = File.ReadAllText(schemaFilePath);
var schema = GraphQLSchemaParser.ParseSchema(schemaContent);

Console.WriteLine($"Loaded schema with:");
Console.WriteLine($"- {schema.Types.Count} types");
Console.WriteLine($"- {schema.Interfaces.Count} interfaces");
Console.WriteLine($"- {schema.Unions.Count} unions");
Console.WriteLine($"- {schema.Enums.Count} enums");
Console.WriteLine($"- {schema.ScalarTypes.Count} scalar types");

// Step 2: Define a GraphQL fragment
string fragmentContent = @"
fragment UserWithPosts on User {
id
name
email
posts {
id
title
content
publishedAt
viewCount
rating
isPublished
tags
categories
}
}
";

// Step 3: Parse the fragment
var fragments = GraphQLParser.ParseContent(fragmentContent);
Console.WriteLine($"\nParsed fragment: {fragments[0].Name} on {fragments[0].OnType}");

// Step 4: Enhance the fragment with schema information
EnhanceFragmentsWithSchema(fragments, schema);

// Step 5: Display the enhanced fragment with type information
var fragment = fragments[0];
Console.WriteLine("\nEnhanced fragment with type information:");

foreach (var field in fragment.Fields)
{
DisplayField(field, 1);
}
}

/// <summary>
/// Enhance fragments with schema information (simplified version)
/// </summary>
private static void EnhanceFragmentsWithSchema(List<GraphQLFragment> fragments, GraphQLSchema schema)
{
foreach (var fragment in fragments)
{
// Skip if the fragment's type doesn't exist in the schema
if (!schema.Types.ContainsKey(fragment.OnType) &&
!schema.Interfaces.ContainsKey(fragment.OnType) &&
!schema.Unions.ContainsKey(fragment.OnType))
{
Console.WriteLine($"Warning: Type {fragment.OnType} not found in schema");
continue;
}

// Enhance fields with schema information
EnhanceFieldsWithSchema(fragment.Fields, fragment.OnType, schema);
}
}

/// <summary>
/// Enhance fields with schema information (simplified version)
/// </summary>
private static void EnhanceFieldsWithSchema(List<GraphQLField> fields, string parentTypeName, GraphQLSchema schema)
{
foreach (var field in fields)
{
// Skip fields with fragment spreads
if (field.FragmentSpreads.Any())
{
continue;
}

// Get field definition from schema
var fieldDefinition = schema.GetFieldDefinition(parentTypeName, field.Name);
if (fieldDefinition != null)
{
// Update field type information
field.Type = fieldDefinition.Type;

// Update deprecation information if not already set
if (!field.IsDeprecated)
{
field.IsDeprecated = fieldDefinition.IsDeprecated;
field.DeprecationReason = fieldDefinition.DeprecationReason;
}

// Recursively enhance nested fields
if (field.SelectionSet.Any() && fieldDefinition.Type != null)
{
string nestedTypeName = GetTypeName(fieldDefinition.Type);
EnhanceFieldsWithSchema(field.SelectionSet, nestedTypeName, schema);
}
}
else
{
Console.WriteLine($"Warning: Field {field.Name} not found in type {parentTypeName}");
}
}
}

/// <summary>
/// Get the base type name from a GraphQL type
/// </summary>
private static string GetTypeName(GraphQLType type)
{
if (type.IsList && type.OfType != null)
{
return GetTypeName(type.OfType);
}
return type.Name;
}

/// <summary>
/// Display a field with its type information
/// </summary>
private static void DisplayField(GraphQLField field, int indentLevel)
{
string indent = new string(' ', indentLevel * 2);
string typeInfo = FormatTypeInfo(field.Type);
string deprecationInfo = field.IsDeprecated ? " @deprecated" + (field.DeprecationReason != null ? $"(reason: \"{field.DeprecationReason}\")" : "") : "";

Console.WriteLine($"{indent}{field.Name}: {typeInfo}{deprecationInfo}");

if (field.SelectionSet.Any())
{
foreach (var nestedField in field.SelectionSet)
{
DisplayField(nestedField, indentLevel + 1);
}
}
}

/// <summary>
/// Format type information for display
/// </summary>
private static string FormatTypeInfo(GraphQLType? type)
{
if (type == null)
{
return "unknown";
}

if (type.IsList)
{
return $"[{FormatTypeInfo(type.OfType)}]{(type.IsNullable ? "" : "!")}";
}

return $"{type.Name}{(type.IsNullable ? "" : "!")}";
}
}
}
116 changes: 116 additions & 0 deletions GraphQLSourceGen.Samples/schema-definition.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# GraphQL Schema Definition

schema {
query: Query
mutation: Mutation
}

type Query {
user(id: ID!): User
users: [User!]!
post(id: ID!): Post
posts: [Post!]!
}

type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post!
deletePost(id: ID!): Boolean!
}

# User type with all fields referenced in fragments
type User {
id: ID!
name: String!
email: String!
isActive: Boolean!
username: String @deprecated(reason: "Use email instead")
oldField: String @deprecated
profile: UserProfile
posts: [Post!]
followers: [User!]
}

# User profile type
type UserProfile {
bio: String
avatarUrl: String
joinDate: DateTime
}

# Post type with all fields referenced in fragments
type Post {
id: ID!
title: String!
content: String
createdAt: DateTime!
publishedAt: DateTime
viewCount: Int
rating: Float
isPublished: Boolean!
tags: [String]
categories: [String!]!
author: User!
comments: [Comment!]
}

# Comment type
type Comment {
id: ID!
text: String!
author: User!
createdAt: DateTime!
}

# Input types
input CreateUserInput {
name: String!
email: String!
password: String!
}

input UpdateUserInput {
name: String
email: String
password: String
isActive: Boolean
}

input CreatePostInput {
title: String!
content: String!
tags: [String]
categories: [String!]
}

input UpdatePostInput {
title: String
content: String
isPublished: Boolean
tags: [String]
categories: [String!]
}

# Custom scalar types
scalar DateTime
scalar Date
scalar Time
scalar Upload

# Interface example
interface Node {
id: ID!
}

# Union example
union SearchResult = User | Post | Comment

# Enum example
enum UserRole {
ADMIN
EDITOR
VIEWER
}
Loading