Skip to content
Open
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
58 changes: 58 additions & 0 deletions Editor/SimpleGraphQL/GraphQLFragmentImporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using SimpleGraphQL.GraphQLParser;
using SimpleGraphQL.GraphQLParser.AST;

// ifdef for different unity versions
#if UNITY_2020_2_OR_NEWER
using UnityEditor.AssetImporters;

#elif UNITY_2017_1_OR_NEWER
using UnityEditor.Experimental.AssetImporters;
#endif

namespace SimpleGraphQL
{
[ScriptedImporter(1, "graphqlfrag")]
public class GraphQLFragmentImporter : ScriptedImporter
{
public override void OnImportAsset(AssetImportContext ctx)
{
var lexer = new Lexer();
var parser = new Parser(lexer);
string contents = File.ReadAllText(ctx.assetPath);
var queryFile = ScriptableObject.CreateInstance<GraphQLFragmentFile>();

GraphQLDocument graphQLDocument = parser.Parse(new Source(contents));

List<GraphQLFragmentDefinition> operations = graphQLDocument.Definitions
.FindAll(x => x.Kind == ASTNodeKind.FragmentDefinition)
.Select(x => (GraphQLFragmentDefinition) x)
.ToList();

if (operations.Count > 0)
{
foreach (GraphQLFragmentDefinition operation in operations)
{
queryFile.Fragment = new Fragment
{
Name = operation.Name?.Value,
TypeCondition = operation.TypeCondition?.Name?.Value,
Source = contents
};
}
}
else
{
throw new ArgumentException(
$"There were no operation definitions inside this graphql: {ctx.assetPath}\nPlease ensure that there is at least one operation defined!");
}

ctx.AddObjectToAsset("FragmentScriptableObject", queryFile);
ctx.SetMainObject(queryFile);
}
}
}
3 changes: 3 additions & 0 deletions Editor/SimpleGraphQL/GraphQLFragmentImporter.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Editor/SimpleGraphQL/GraphQLImporterV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,15 @@ public override void OnImportAsset(AssetImportContext ctx)
Debug.LogWarning("Unable to convert operation type in " + ctx.assetPath);
}

var fragments = operation.Descendants().Where(node => node is GraphQLFragmentSpread).Select(node => ((GraphQLFragmentSpread)node).Name.Value).ToArray();

queryFile.Queries.Add(new Query
{
FileName = fileName,
OperationName = operation.Name?.Value,
OperationType = operationType,
Source = contents
Source = contents,
Fragments = fragments
});
}
}
Expand Down
40 changes: 38 additions & 2 deletions Plugins/SimpleGraphQL/GraphQLParser/AST/ASTNode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace SimpleGraphQL.GraphQLParser.AST
using System.Collections.Generic;
using System.Linq;

namespace SimpleGraphQL.GraphQLParser.AST
{
public abstract class ASTNode
{
Expand All @@ -7,5 +10,38 @@ public abstract class ASTNode
public GraphQLLocation Location { get; set; }

public GraphQLComment Comment { get; set; }

}

public static class ASTNodeExtensions
{
public static IEnumerable<ASTNode> Descendants(this ASTNode root)
{
var nodes = new Stack<ASTNode>(new[] {root});
while (nodes.Any())
{
ASTNode node = nodes.Pop();
yield return node;

GraphQLSelectionSet selectionSet = null;

if (node is GraphQLFieldSelection fieldSelection)
{
selectionSet = fieldSelection.SelectionSet;
}

if (node is GraphQLOperationDefinition operationDefinition)
{
selectionSet = operationDefinition.SelectionSet;
}

if(selectionSet != null && selectionSet.Selections != null)
{
foreach (var n in selectionSet.Selections)
nodes.Push(n);
}
}
}

}
}
}
52 changes: 52 additions & 0 deletions Runtime/SimpleGraphQL/Fragment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using JetBrains.Annotations;
using UnityEngine;

namespace SimpleGraphQL
{
[PublicAPI]
[Serializable]
public class Fragment
{
/// <summary>
/// The name of the fragment.
/// </summary>
[CanBeNull]
public string Name;

/// <summary>
/// The type the fragment is selecting from.
/// </summary>
public string TypeCondition;

/// <summary>
/// The actual fragment itself.
/// </summary>
[TextArea]
public string Source;

public override string ToString()
{
return $"fragment {Name} on {TypeCondition}";
}

protected bool Equals(Fragment other)
{
return Name == other.Name;
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Fragment)obj);
}

public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}

}
3 changes: 3 additions & 0 deletions Runtime/SimpleGraphQL/Fragment.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 25 additions & 2 deletions Runtime/SimpleGraphQL/GraphQLClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class GraphQLClient
{
public readonly List<Query> SearchableQueries;
public readonly Dictionary<string, string> CustomHeaders;
public readonly Dictionary<string, Fragment> Fragments;

public string Endpoint;
public string AuthScheme;
Expand All @@ -24,13 +25,14 @@ public GraphQLClient(
string endpoint,
IEnumerable<Query> queries = null,
Dictionary<string, string> headers = null,
string authScheme = null
)
string authScheme = null,
IEnumerable<Fragment> fragments = null)
{
Endpoint = endpoint;
AuthScheme = authScheme;
SearchableQueries = queries?.ToList();
CustomHeaders = headers;
Fragments = fragments?.ToDictionary(fragment => fragment.Name);
}

public GraphQLClient(GraphQLConfig config)
Expand All @@ -39,6 +41,7 @@ public GraphQLClient(GraphQLConfig config)
SearchableQueries = config.Files.SelectMany(x => x.Queries).ToList();
CustomHeaders = config.CustomHeaders.ToDictionary(header => header.Key, header => header.Value);
AuthScheme = config.AuthScheme;
Fragments = config.Fragments.ToDictionary(file => file.Fragment.Name, file => file.Fragment);
}

/// <summary>
Expand Down Expand Up @@ -342,5 +345,25 @@ public List<Query> FindQueriesByOperation(string operation)
{
return SearchableQueries?.FindAll(x => x.OperationName == operation);
}

public List<Fragment> FindFragments(IEnumerable<string> fragmentNames)
{
List<Fragment> fragments = new();

if (fragmentNames == null)
{
return fragments;
}

foreach (var fragmentName in fragmentNames)
{
if (Fragments.TryGetValue(fragmentName, out var fragment))
{
fragments.Add(fragment);
}
}

return fragments;
}
}
}
3 changes: 3 additions & 0 deletions Runtime/SimpleGraphQL/GraphQLConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public class GraphQLConfig : ScriptableObject
[Header(".graphql Files")]
public List<GraphQLFile> Files;

[Header("Fragment files")]
public List<GraphQLFragmentFile> Fragments;

/// <summary>
/// Set the auth scheme to be used here if you need authentication.
/// You can also use CustomHeaders to pass in authentication if needed, but this is inherently less secure.
Expand Down
10 changes: 10 additions & 0 deletions Runtime/SimpleGraphQL/GraphQLFragmentFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;
using UnityEngine;

namespace SimpleGraphQL
{
public class GraphQLFragmentFile : ScriptableObject
{
public Fragment Fragment;
}
}
3 changes: 3 additions & 0 deletions Runtime/SimpleGraphQL/GraphQLFragmentFile.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 23 additions & 6 deletions Runtime/SimpleGraphQL/Query.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;

namespace SimpleGraphQL
Expand Down Expand Up @@ -32,6 +34,11 @@ public class Query
/// </summary>
public string Source;

/// <summary>
/// Fragments used by this query
/// </summary>
public string[] Fragments;

public override string ToString()
{
return $"{FileName}:{OperationName}:{OperationType}";
Expand All @@ -41,14 +48,24 @@ public override string ToString()
[PublicAPI]
public static class QueryExtensions
{
public static Request ToRequest(this Query query, object variables = null)
public static Request ToRequest(this Query query, object variables = null, IEnumerable<Fragment> fragments = null)
{
return new Request
StringBuilder querySource = new StringBuilder(query.Source);

if (fragments != null)
{
Query = query.Source,
Variables = variables,
OperationName = query.OperationName,
};
foreach (var fragment in fragments)
{
querySource.AppendLine(fragment?.Source);
}
}

return new Request
{
Query = querySource.ToString(),
Variables = variables,
OperationName = query.OperationName,
};
}
}

Expand Down