GQLIntrospection.java

package net.morimekta.providence.graphql.gql;

import net.morimekta.providence.PMessage;
import net.morimekta.providence.descriptor.PDescriptor;
import net.morimekta.providence.descriptor.PField;
import net.morimekta.providence.descriptor.PPrimitive;
import net.morimekta.providence.descriptor.PStructDescriptor;
import net.morimekta.providence.graphql.introspection.Schema;
import net.morimekta.providence.graphql.introspection.Type;
import net.morimekta.providence.graphql.introspection.TypeArguments;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import java.util.List;

import static net.morimekta.providence.graphql.gql.GQLUtil.toArgumentString;

/**
 * A introspection is very much like a 'normal' field, but
 * has special meaning to graphql. Note that <b>all</b> field
 * names that start with <code>__</code> are reserved for
 * introspection.
 *
 * {@inheritDoc}
 */
@Immutable
public class GQLIntrospection implements GQLSelection {
    private final List<GQLSelection> selectionSet;
    private final PMessage           arguments;
    private final Field              field;
    private final String             alias;

    /**
     * Introspeciton fields supported by providence-graphql.
     */
    public enum Field {
        __type(TypeArguments.kDescriptor, Type.kDescriptor),
        __typename(null, PPrimitive.STRING),
        __schema(null, Schema.kDescriptor),
        ;

        public final PStructDescriptor<?>  arguments;
        public final PDescriptor response;

        Field(PStructDescriptor<?> arguments, PDescriptor response) {
            this.arguments = arguments;
            this.response = response;
        }
    }

    /**
     * Find introspection field by name.
     *
     * @param name The name of field to find.
     * @return The introspection field, or null if not found.
     */
    public static Field findFieldByName(String name) {
        for (Field field : Field.values()) {
            if (field.name().equals(name)) {
                return field;
            }
        }
        return null;
    }

    /**
     * @param field Name of introspection field.
     * @param alias Introspection alias. Only possible / allowed
     *             on root type introspection.
     * @param arguments Arguments for general introspection.
     * @param selectionSet The introspection selection set.
     */
    public GQLIntrospection(@Nonnull Field field,
                            @Nullable String alias,
                            @Nullable PMessage<?> arguments,
                            @Nullable List<GQLSelection> selectionSet) {
        this.field = field;
        this.alias = alias;
        this.arguments = arguments;
        this.selectionSet = selectionSet;
    }

    @Nullable
    public PMessage getArguments() {
        return arguments;
    }

    @Nullable
    public String getAlias() {
        return alias;
    }

    @Nonnull
    public Field getField() {
        return field;
    }

    @Override
    public List<GQLSelection> getSelectionSet() {
        return selectionSet;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        if (alias != null) {
            builder.append(alias).append(": ");
        }
        builder.append(field.name());

        if (arguments != null) {
            builder.append("(");
            boolean first = true;
            for (PField pf : arguments.descriptor().getFields()) {
                if (arguments.has(pf.getId())) {
                    if (!first) {
                        builder.append(", ");
                    } else {
                        first = false;
                    }
                    builder.append(pf.getName())
                           .append(": ")
                           .append(toArgumentString(arguments.get(pf.getId())));
                }
            }
            builder.append(")");
        }
        if (selectionSet != null && selectionSet.size() > 0) {
            if (arguments != null) {
                builder.append(" ");
            }

            builder.append("{");
            boolean first = true;
            for (GQLSelection field : selectionSet) {
                if (!first) {
                    builder.append(", ");
                } else {
                    first = false;
                }
                builder.append(field.toString());
            }
            builder.append("}");
        }
        return builder.toString();
    }
}