byme8 / ZeroQL

C# GraphQL client with Linq-like syntax
MIT License
278 stars 13 forks source link

Custom scalars #45

Closed lukemurray closed 1 year ago

lukemurray commented 1 year ago

Is there a way to define custom scalars? A simple mapping would be fine

I.e. map a gql defined scalar Date to dotnet DateTime.

Also, we have a scalar defined Point and it just returns the json of the point {x: 1, y: 1}. It would be good to be able to map that to a C# class

public class Point {
  public int X { get; set; }
  public int Y { get; set; }
}

I couldn't find anything in the documentation about this. And the generated scalars just store the returned string value.

Thanks

byme8 commented 1 year ago

At the moment, there is no way to define your own. I will have a look at it.

lukemurray commented 1 year ago

I do this in a similar library I made by just a simple mapping - was looking to move to this as it is similar and you've implemented the tool and auto generation on build which is what I wanted to have :)

byme8 commented 1 year ago

Checkout v4.1.0-preview.2. Now it is possible to create your own scalars. For example, we have a schema with scalar Instant:

schema {
  query: Query
}

type Query {
  instant: Instant!
}

"Represents an instant on the global timeline, with nanosecond resolution."
scalar Instant

Now we can create a C# class and JSON serializer for it:

public class Instant
{
    public DateTimeOffset DateTimeOffset { get; set; }
}

public class InstantConverter : JsonConverter<Instant?>
{
    public override Instant? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var instant = reader.GetString();
        return new Instant { DateTimeOffset = DateTimeOffset.Parse(instant!) };
    }

    public override void Write(Utf8JsonWriter writer, Instant? value, JsonSerializerOptions options)
    {
        var instant = value.DateTimeOffset.ToString("yyyy-MM-ddTHH:mm:ssZ");
        writer.WriteStringValue(instant);
    }
}

public static class Module
{
    [ModuleInitializer]
    public static void Initialize()
    {
        // dont forget to add serializer to ZeroQL json options
        ZeroQLJsonOptions.Configure(o => o.Converters.Add(new InstantConverter()));
    }
}

Then during generation, you can pass parameter --scalars Instant=MyApp.Instant or set property scalars in zeroql.json config file

"scalars": {
    "Instant": "MyApp.Instant"
}

It will replace the GraphQL scalar with the correct type in generated GraphQL client.

lukemurray commented 1 year ago

Thanks, this does look like it will do we we need. I'll give it a try and let you know of any issues.

Thanks