io.engagingspaces.graphql.marshaller.schema.decorators.GraphQLSchemaDO.java Source code

Java tutorial

Introduction

Here is the source code for io.engagingspaces.graphql.marshaller.schema.decorators.GraphQLSchemaDO.java

Source

/*
 * Copyright (c) 2016 The original author or authors
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *      The Eclipse Public License is available at
 *      http://www.eclipse.org/legal/epl-v10.html
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */

package io.engagingspaces.graphql.marshaller.schema.decorators;

import graphql.schema.*;
import io.engagingspaces.graphql.marshaller.json.JsonReference;
import io.engagingspaces.graphql.marshaller.json.impl.JsonObjectHelper;
import io.engagingspaces.graphql.marshaller.schema.Marshaller;
import io.engagingspaces.graphql.marshaller.schema.SchemaContext;
import io.engagingspaces.graphql.marshaller.schema.SchemaDecorator;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static io.engagingspaces.graphql.marshaller.json.PropNames.*;
import static io.engagingspaces.graphql.marshaller.schema.SchemaContext.EMPTY;

/**
 * Data object wrapper for {@link GraphQLSchema}.
 *
 * @author <a href="https://github.com/aschrijver/">Arnold Schrijver</a>
 */
public class GraphQLSchemaDO extends GraphQLSchema implements SchemaDecorator<GraphQLSchema> {

    public static final List<String> introspectionTypes = Collections.unmodifiableList(Arrays.asList("__Schema",
            "__Type", "__TypeKind", "__Field", "__InputValue", "__EnumValue", "__Directive"));

    private final GraphQLSchema schema;
    private final JsonObject rootJson;
    private final JsonObject schemaJson;
    private final SchemaContext context;
    private final JsonReference jsonReference;

    private final Predicate<GraphQLType> shouldIncludeIntrospectionTypes;
    private final Predicate<Object> shouldIncludeDirectives;

    private GraphQLSchemaDO(GraphQLSchema schema, JsonObject json, SchemaContext context) {
        super(new GraphQLObjectType(EMPTY, null, Collections.emptyList(), Collections.emptyList()));
        this.schema = schema;
        this.rootJson = json;
        this.schemaJson = json == null ? null : getSchemaJson(json);
        this.context = context;
        this.shouldIncludeDirectives = directive -> context.options().includeDirectives();
        this.shouldIncludeIntrospectionTypes = type -> context.options().includeIntrospectionTypes()
                || !introspectionTypes.contains(type.getName());

        unmarshallSchemaObjects(rootJson);

        this.jsonReference = context.registerDecorator(this);
    }

    protected GraphQLSchemaDO(GraphQLSchema schema, SchemaContext context) {
        this(schema, null, context);
    }

    /**
     * Creates the GraphQL object from its json serialization data using the provided schema context.
     *
     * @param json    the json data
     * @param context the schema context, created if not provided
     */
    @SuppressWarnings("unused")
    public GraphQLSchemaDO(JsonObject json, SchemaContext context) {
        this(null, json, context);
    }

    /**
     * Creates a decorated version of the GraphQL schema that is passed in.
     *
     * @param schema the original schema
     * @return the decorated schema
     */
    public static GraphQLSchema of(GraphQLSchema schema) {
        return new GraphQLSchemaDO(schema, Marshaller.createContext());
    }

    /**
     * Creates a decorated version of the GraphQL schema that is passed in, using the provided context.
     *
     * @param schema  the original schema
     * @param context the schema context
     * @return the decorated schema
     */
    public static GraphQLSchema of(GraphQLSchema schema, SchemaContext context) {
        return new GraphQLSchemaDO(schema, Marshaller.createContextIfMissing(context));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JsonObject toJson() {
        return toJson(false);
    }

    public JsonObject toJson(boolean shallow) {
        if (shallow) {
            return marshallShallow();
        }
        if (schema == null) {
            return schemaJson;
        }
        JsonObject schemaJson = new JsonObject();
        marshallTypes(schemaJson);
        marshallTypeResolvers(schemaJson);
        marshallDataFetchers(schemaJson);
        marshallScalarTypes(schemaJson);
        return marshallSchemaEntry(schemaJson);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public JsonReference jsonReference() {
        return jsonReference;
    }

    @Override
    public Set<GraphQLType> getDictionary() {
        if (schema == null) {
            return schemaJson.getJsonArray(DICTIONARY, new JsonArray()).stream()
                    .map(type -> (GraphQLType) context.unmarshall((JsonObject) type)).collect(Collectors.toSet());
        }
        return schema.getDictionary().stream().map(context::decoratorOf).collect(Collectors.toSet());
    }

    @Override
    public GraphQLType getType(String typeName) {
        if (schema == null) {
            return context
                    .unmarshall(rootJson.getJsonObject(SCHEMA_TYPES, new JsonObject()).getJsonObject(typeName));
        }
        return context.decoratorOf(schema.getType(typeName));
    }

    @Override
    public List<GraphQLType> getAllTypesAsList() {
        if (schema == null) {
            return context.unmarshallList(rootJson, SCHEMA_TYPES);
        }
        return schema.getAllTypesAsList().stream().map(context::decoratorOf).collect(Collectors.toList());
    }

    @Override
    public GraphQLObjectType getQueryType() {
        if (context == null) {
            // called during dummy super() constructor initialization.
            return new GraphQLObjectType(EMPTY, null, Collections.emptyList(), Collections.emptyList());
        }
        return schema == null ? context.unmarshall(schemaJson.getJsonObject(QUERY_TYPE))
                : context.decoratorOf(schema.getQueryType());
    }

    @Override
    public GraphQLObjectType getMutationType() {
        return isSupportingMutations()
                ? (schema == null ? context.unmarshall(schemaJson.getJsonObject(MUTATION_TYPE))
                        : context.decoratorOf(schema.getMutationType()))
                : null;
    }

    @Override
    public List<GraphQLDirective> getDirectives() {
        if (schema == null) {
            return context.unmarshallList(schemaJson, DIRECTIVES, this);
        }
        return schema.getDirectives().stream().map(directive -> context.decoratorOf(directive, this))
                .collect(Collectors.toList());
    }

    @Override
    public GraphQLDirective getDirective(String name) {
        if (schema == null) {
            return context.unmarshall(schemaJson.getJsonObject(DIRECTIVES, new JsonObject()).getJsonObject(name),
                    this);
        }
        return context.decoratorOf(schema.getDirective(name));
    }

    @Override
    public boolean isSupportingMutations() {
        if (context == null) {
            return false;
        }
        if (schema == null) {
            return schemaJson.containsKey(MUTATION_TYPE)
                    && schemaJson.getValue(MUTATION_TYPE) instanceof JsonObject;
        }
        return schema.isSupportingMutations();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public GraphQLSchema original() {
        return schema;
    }

    private JsonObject marshallShallow() {
        if (schema == null) {
            return schemaJson;
        }
        return JsonObjectHelper.jsonObject().put(MARSHALED_TYPE, GraphQLSchema.class.getName())
                .put(QUERY_TYPE, context.referenceTo(getQueryType()))
                .putIfPresent(MUTATION_TYPE,
                        isSupportingMutations() ? context.referenceTo(getMutationType()) : null)
                .putIf(DIRECTIVES, getDirectives().stream().map(context::marshall).collect(Collectors.toList()),
                        test -> context.options().includeDirectives())
                .putIfPresent(DICTIONARY,
                        getDictionary().stream().map(context::marshall).collect(Collectors.toList()));
    }

    private void marshallTypes(JsonObject schemaJson) {
        JsonObject types = new JsonObject();
        JsonObject interfaces = new JsonObject();

        getAllTypesAsList().stream().filter(shouldIncludeIntrospectionTypes)
                .filter(type -> !(type instanceof GraphQLScalarType)).map(context::marshall).forEach(type -> {
                    if (GraphQLInterfaceType.class.getName().equals(type.getString(MARSHALED_TYPE))) {
                        interfaces.put(type.getString(NAME), type);
                    } else {
                        types.put(type.getString(NAME), type);
                    }
                });
        schemaJson.put(SCHEMA_TYPES, types);
        schemaJson.put(SCHEMA_INTERFACES, interfaces);
    }

    private void marshallTypeResolvers(JsonObject schemaJson) {
        if (!context.getTypeResolvers().isEmpty()) {
            JsonObject resolvers = new JsonObject();
            context.getTypeResolvers().entrySet()
                    .forEach(entry -> resolvers.put(entry.getKey(), context.marshall(entry.getValue())));
            schemaJson.put(TYPE_RESOLVERS, resolvers);
        }
    }

    private void marshallDataFetchers(JsonObject schemaJson) {
        if (!context.getDataFetchers().isEmpty()) {
            JsonObject fetchers = new JsonObject();
            context.getDataFetchers().entrySet()
                    .forEach(entry -> fetchers.put(entry.getKey(), context.marshall(entry.getValue())));
            schemaJson.put(DATA_FETCHERS, fetchers);
        }

    }

    private void marshallScalarTypes(JsonObject schemaJson) {
        if (!context.getScalarTypes().isEmpty()) {
            JsonObject scalars = new JsonObjectHelper();
            context.getScalarTypes().entrySet()
                    .forEach(entry -> scalars.put(entry.getKey(), context.marshall(entry.getValue())));
            schemaJson.put(SCALAR_TYPES, scalars);
        }
    }

    private JsonObject marshallSchemaEntry(JsonObject schemaJson) {
        return schemaJson.put(SCHEMAS, new JsonObject().put(jsonReference.getTargetKey(), toJson(true)));
    }

    private JsonObject getSchemaJson(JsonObject rootJson) {
        Set<String> schemas = rootJson.getJsonObject(SCHEMAS, new JsonObject()).fieldNames();
        if (schemas.size() == 0 || schemas.size() > 1) {
            throw new IllegalArgumentException("Failed to unmarshall. Expected 1 schema, found: " + schemas.size());
        }
        String schemaName = schemas.iterator().next();
        return rootJson.getJsonObject(SCHEMAS).getJsonObject(schemaName);
    }

    private void unmarshallSchemaObjects(JsonObject json) {
        if (json != null) {
            context.unmarshallList(json, SCHEMA_TYPES);
            context.unmarshallList(json, SCHEMA_INTERFACES);
            context.unmarshallList(json, SCALAR_TYPES);
            schemaJson.getJsonObject(DIRECTIVES, new JsonObject()).stream().filter(shouldIncludeDirectives)
                    .forEach(entry -> context.unmarshall((JsonObject) entry.getValue(), this));
        }
    }
}