smallrye / smallrye-graphql

Implementation for MicroProfile GraphQL
Apache License 2.0
160 stars 91 forks source link

add custom scalar #911

Open Felk opened 3 years ago

Felk commented 3 years ago

I am having trouble getting a custom scalar to work properly. Since there does not appear to be first-class support for custom scalars in neither microprofile-graphql nor in smallrye-graphql I attempted to register a custom scalar using the underlying graphql-java library using the same name as the respective java class.

public GraphQLSchema.Builder extendGraphQLSchema(@Observes GraphQLSchema.Builder builder) {
    GraphQLScalarType customScalar = GraphQLScalarType.newScalar()
            .name("MyFancyScalar")
            .coercing(new MyFancyScalarCoercing())
            .build();
    return builder.additionalType(customScalar);
}

The custom scalar is very simple:

public class MyFancyScalar {
    private final String content;
    public MyFancyScalar(String content) {
        this.content = content;
    }
    public String getContent() {
        return content;
    }
}

If I now try to use the custom scalar as-is, e.g. like this

@Query
public String myEchoQuery(MyFancyScalar someScalar) {
    return someScalar.getContent();
}

I get the following error at quarkus compile time:

Caused by: java.lang.IllegalArgumentException: Class com.example.MyFancyScalar is used as input, but does neither have a public default constructor nor a JsonbCreator method

But I did not intend to use it as an input, I intended it to be used as a scalar. Weirdly enough, if I use e.g. YearMonth I can use it as a scalar using the above method just fine:

public GraphQLSchema.Builder extendGraphQLSchema(@Observes GraphQLSchema.Builder builder) {
    GraphQLScalarType yearMonthScalar = GraphQLScalarType.newScalar()
            .name("YearMonth")
            .coercing(new YearMonthCoercing())
            .build();
    return builder.additionalType(yearMonthScalar);
}
@Query
public String myEchoQuery(YearMonth someYearMonth) {
    return someYearMonth.toString();
}

although I get this warning:

2021-07-12 16:52:07,066 WARN [io.sma.gra.sch.cre.ReferenceCreator] (build-16) Class [java.time.YearMonth] is not indexed in Jandex. Can not scan Object Type, might not be mapped correctly. Kind = [CLASS]

I am aware of @ToScalar but was wondering if there is a way to add a custom scalar so that it appears as a true custom GraphQL scalar in the schema instead of just String?

t1 commented 3 years ago

I suppose this is related to https://github.com/eclipse/microprofile-graphql/issues/94

phillip-kruger commented 3 years ago

I guess, at this point, you would also need to update the myEchoQuery, your interceptor added the type, but did not update the query. As @t1 mentioned, we want to add a easy way to do this, but we have not yet gotten around to do it.

robp94 commented 2 years ago

Any news on this?

phillip-kruger commented 2 years ago

No progress yet. PRs welcome :)

pipinet commented 2 years ago

Any news on this?

phillip-kruger commented 2 years ago

No news, but you are welcome to provide a PR and we can discuss further there?

BobanNikolov commented 1 year ago

Any news on this?

t1 commented 1 year ago

Just to make it clear: it's easy to use custom scalars as return types with the @AdaptToScalar annotation. But it's currently not possible to use as an Input... we'd need something like a @AdaptFromScalar or so.

BobanNikolov commented 1 year ago

I need it precisely for ArrayList. Tried today some workarounds but no progress so far. If I manage to do something, I will get back to you,

BobanNikolov commented 1 year ago

Luckily, I found a workaround. For a reference I am using the quarkus implementation of the smallrye-graphql, but that shouldn't be much of a info, as it's build upon smallrye-graphql. I annotated the class with the annotation @Type which makes my class "viewable" to the GraphQL schema. In my class beforehand I had ArrayList collection and it was giving me the same error Type ArrayList not found in schema . Now I changed it to List<String> collection and that will only make my case work when transfering list of strings (which I am not completely satisfied with, but does the case for now). I would suggest to make a ScalarType Collection like we now have BigDecimal, String, etc.