All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.basestar.graphql.wiring.RuntimeWiringFactory Maven / Gradle / Ivy

package io.basestar.graphql.wiring;

/*-
 * #%L
 * basestar-graphql
 * %%
 * Copyright (C) 2019 - 2020 Basestar.IO
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import graphql.TypeResolutionEnvironment;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLObjectType;
import graphql.schema.TypeResolver;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.TypeRuntimeWiring;
import io.basestar.auth.Caller;
import io.basestar.database.Database;
import io.basestar.database.options.*;
import io.basestar.expression.Expression;
import io.basestar.graphql.GraphQLUtils;
import io.basestar.schema.*;
import io.basestar.util.Path;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@Deprecated
public class RuntimeWiringFactory {

    private final Database database;

    private final Namespace namespace;

    public RuntimeWiringFactory(final Database database, final Namespace namespace) {

        this.database = database;
        this.namespace = namespace;
    }

    public RuntimeWiring runtimeWiring() {

        final RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
        builder.type(TypeRuntimeWiring.newTypeWiring("Query")
                .dataFetchers(queryFetchers()));
        builder.type(TypeRuntimeWiring.newTypeWiring("Mutation")
                .dataFetchers(mutationFetchers()));
        namespace.getSchemas().forEach((k, schema) -> {
            if(schema instanceof InstanceSchema) {
                if(!((InstanceSchema) schema).isConcrete()) {
                    builder.type(TypeRuntimeWiring.newTypeWiring(schema.getName())
                            .typeResolver(InterfaceResolver.INSTANCE));
                }
            }
        });
        return builder.build();
    }

    @SuppressWarnings("rawtypes")
    private Map queryFetchers() {

        final Map results = new HashMap<>();
        namespace.getSchemas().forEach((schemaName, schema) -> {
            if(schema instanceof ObjectSchema) {
                final ObjectSchema objectSchema = (ObjectSchema)schema;
                results.put("read" + objectSchema.getName(), getFetcher(objectSchema));
                results.put("query" + objectSchema.getName(), queryFetcher(objectSchema));
                objectSchema.getAllLinks().forEach((linkName, link) -> {
                    final String name = "query" + schemaName + GraphQLUtils.ucFirst(linkName);
                    results.put(name, queryLinkFetcher(objectSchema, link));
                });
            }
        });
        return results;
    }

    private DataFetcher> getFetcher(final ObjectSchema schema) {

        return (env) -> {
            final Caller caller = GraphQLUtils.caller(env.getContext());
            final Set paths = paths(env);
            final Set expand = schema.requiredExpand(paths);
            final String id = env.getArgument(Reserved.ID);
            final Long version = env.getArgumentOrDefault(Reserved.VERSION, null);
            final ReadOptions options = ReadOptions.builder()
                    .schema(schema.getName()).id(id)
                    .version(version).expand(expand)
                    .build();
            return database.read(caller, options)
                    .thenApply(object -> GraphQLUtils.toResponse(schema, object));
        };
    }

    private DataFetcher> queryFetcher(final ObjectSchema schema) {

        return (env) -> {
            final Caller caller = GraphQLUtils.caller(env.getContext());
            final Set paths = paths(env);
            final Set expand = schema.requiredExpand(paths);
            final String query = env.getArgument("query");
            final QueryOptions options = QueryOptions.builder()
                    .schema(schema.getName())
                    .expression(Expression.parse(query))
                    .expand(expand)
                    .build();
            return database.query(caller, options)
                    .thenApply(objects -> objects.map(object -> GraphQLUtils.toResponse(schema, object)));
        };
    }

    private DataFetcher> queryLinkFetcher(final ObjectSchema schema, final Link link) {

        return (env) -> {

            final Caller caller = GraphQLUtils.caller(env.getContext());
            final ObjectSchema linkSchema = link.getSchema();
            final Set paths = paths(env);
            final Set expand = linkSchema.requiredExpand(paths);
            final String id = env.getArgument(Reserved.ID);
            final QueryLinkOptions options = QueryLinkOptions.builder()
                    .schema(schema.getName())
                    .link(link.getName())
                    .id(id)
                    .expand(expand)
                    .build();
            return database.queryLink(caller, options)
                    .thenApply(objects -> objects.map(object -> GraphQLUtils.toResponse(linkSchema, object)));
        };
    }

    @SuppressWarnings("rawtypes")
    private Map mutationFetchers() {

        final Map results = new HashMap<>();
        namespace.getSchemas().forEach((k, schema) -> {
            if(schema instanceof ObjectSchema) {
                final ObjectSchema objectSchema = (ObjectSchema)schema;
                results.put("create" + objectSchema.getName(), createFetcher(objectSchema));
                results.put("update" + objectSchema.getName(), updateFetcher(objectSchema));
            }
        });
        return results;
    }

    private DataFetcher> createFetcher(final ObjectSchema schema) {

        return (env) -> {
            final Caller caller = GraphQLUtils.caller(env.getContext());
            final Set paths = paths(env);
            final Set expand = schema.requiredExpand(paths);
            final String id = env.getArgumentOrDefault(Reserved.ID, null);
            final Map data = GraphQLUtils.fromRequest(schema, env.getArgument("data"));
            final CreateOptions options = CreateOptions.builder()
                    .schema(schema.getName()).id(id)
                    .data(data).expand(expand)
                    .build();
            return database.create(caller, options)
                    .thenApply(object -> GraphQLUtils.toResponse(schema, object));
        };
    }

    private DataFetcher> updateFetcher(final ObjectSchema schema) {

        return (env) -> {
            final Caller caller = GraphQLUtils.caller(env.getContext());
            final Set paths = paths(env);
            final Set expand = schema.requiredExpand(paths);
            final String id = env.getArgument(Reserved.ID);
            final Long version = env.getArgumentOrDefault(Reserved.VERSION, null);
            final Map data = GraphQLUtils.fromRequest(schema, env.getArgument("data"));
            final UpdateOptions options = UpdateOptions.builder()
                    .schema(schema.getName()).id(id)
                    .data(data).version(version)
                    .expand(expand)
                    .build();
            return database.update(caller, options)
                    .thenApply(object -> GraphQLUtils.toResponse(schema, object));
        };
    }

    private static Set paths(final DataFetchingEnvironment env) {

        return env.getSelectionSet().getFields().stream()
                .map(v -> path(v.getQualifiedName()))
                .collect(Collectors.toSet());
    }

    private static Path path(final String name) {

        // FIXME: if we do this properly then we don't need to use the reserved prefix in map key/value
        return Path.of(Arrays.stream(name.split("/"))
                .map(v -> {
                    if(v.equals(GraphQLUtils.MAP_VALUE)) {
                        return "*";
                    } else {
                        return v;
                    }
                }).filter(v -> !v.startsWith(Reserved.PREFIX))
                .toArray(String[]::new));
    }

    private static class InterfaceResolver implements TypeResolver {

        public static final InterfaceResolver INSTANCE = new InterfaceResolver();

        @Override
        public GraphQLObjectType getType(final TypeResolutionEnvironment env) {

            final Map object = env.getObject();
            return env.getSchema().getObjectType(Instance.getSchema(object));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy