4

I try to add a custom scalar type for GraphQL Java. I need it to resolve a Map without creating a type for it because it is a common return type in my logic.

I followed the instruction (here: http://graphql-java.readthedocs.io/en/latest/scalars.html) to create a scalar type.

This is my MapScalar.java

public class MapScalar {
  private static final Logger LOG = LoggerFactory.getLogger(MapScalar.class);

  public static final GraphQLScalarType MAP = new GraphQLScalarType("Map", "A custom map scalar type", new Coercing() {
    @Override
    public Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
      Map map = null;
      try {
        map = Map.class.cast(dataFetcherResult);
      } catch (ClassCastException exception) {
        throw new CoercingSerializeException("Could not convert " + dataFetcherResult + " into a Map", exception);
      }
      return map;
    }

    @Override
    public Object parseValue(Object input) throws CoercingParseValueException {
      LOG.warn("parseValue called");
      return null;
    }

    @Override
    public Object parseLiteral(Object input) throws CoercingParseLiteralException {
      LOG.warn("parseLiteral called");
      return null;
    }
  });
}

I added this scalar instance to RunTimeWiring

final RuntimeWiring runtimeWiring = newRuntimeWiring()
        .type(queryTypeFactory.getQueryBaseQueryType()) // just convenience methods I made
        .type(queryTypeFactory.getPageQueryType(viewName)) // ...
        .type(queryTypeFactory.getContentQueryType(viewName)) // ...
        .type(queryTypeFactory.getPictureQueryType()) // ...
        .type(queryTypeFactory.getSettingQueryType()) // just convenience methods I made
        .scalar(MapScalar.MAP) // added new scalar here
        .build();

I defined this MapDataFetcher

@Component
public class MapDataFetcher implements DataFetcher {

  @Override
  public Object get(DataFetchingEnvironment environment) {
    String fieldName = environment.getField().getName();
    Content source = Content.class.cast(environment.getSource());
    return source.getStruct(fieldName).toNestedMaps(); // returns a Map<String,Object>
  }

}

And the schema for this field/scalar is defined as followed:

type Content {
    //... other fields
    settings: Map
}

When debugging the RunTimeWiring everthing seems fine. The scalar has been added to the default scalars:

enter image description here

Still this error occurs:

SCHWERWIEGEND: Servlet.service() for servlet [cae] in context with path [/blueprint] threw exception [Request processing failed; nested exception is SchemaProblem{errors=[The field type 'Map' is not present when resolving type 'Content' [@10:1], The field type 'Map' is not present when resolving type 'Setting' [@28:1]]}] with root cause
SchemaProblem{errors=[The field type 'Map' is not present when resolving type 'Content' [@10:1], The field type 'Map' is not present when resolving type 'Setting' [@28:1]]}

I can not find any hind in the tutorials to find out what I am missing out to make it work. I understand that there is a missing type here. But creating a new Type with newRunTimeWiring().type() is a for creating Non-Scalar types isn't it? Or do I still need to create a Map type in there?

xetra11
  • 7,671
  • 14
  • 84
  • 159
  • Your implementation is quite broken in that this scalar will be silently ignored if used as input directly or as a variable. See a more complete implementation [here](https://stackoverflow.com/a/46074133/294657). Also, why the funky `Map.class.cast(dataFetcherResult)`? What's wrong with simple `(Map) dataFetcherResult`? – kaqqao May 14 '18 at 22:43
  • I can read it better and am reminded to use it when having Java Streams API and method references `stream().map(Map.class::cast)` – xetra11 May 15 '18 at 04:11

1 Answers1

10

There was just one little thing missing nobody mentioned in the tutorials. I had to add this so Map is actually recognized as a scala type

add to schema definition file:

scalar Map
xetra11
  • 7,671
  • 14
  • 84
  • 159
  • thanks. that's exactly what I was looking for. you should select your answer . – Doua Beri May 17 '18 at 23:05
  • It is actually documented in https://github.com/graphql-java/graphql-java-extended-scalars, it says pretty straight forward, after you register the scalar with graphql-java, you need to use it in your schema. – Kobynet Oct 01 '20 at 17:35