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

io.smallrye.graphql.mavenplugin.GenerateSchemaMojo Maven / Gradle / Ivy

There is a newer version: 2.11.0
Show newest version
package io.smallrye.graphql.mavenplugin;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.jboss.jandex.CompositeIndex;
import org.jboss.jandex.Index;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.JarIndexer;
import org.jboss.jandex.Result;

import graphql.schema.GraphQLSchema;
import io.smallrye.graphql.bootstrap.Bootstrap;
import io.smallrye.graphql.execution.SchemaPrinter;
import io.smallrye.graphql.schema.SchemaBuilder;
import io.smallrye.graphql.schema.helper.TypeAutoNameStrategy;
import io.smallrye.graphql.schema.model.Schema;

@Mojo(name = "generate-schema", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class GenerateSchemaMojo extends AbstractMojo {
    private static MavenConfig mavenConfig;

    /**
     * Destination file where to output the schema.
     * If no path is specified, the schema will be printed to the log.
     */
    @Parameter(defaultValue = "${project.build.directory}/generated/schema.graphql", property = "destination")
    private String destination;

    /**
     * Scan project's dependencies for GraphQL model classes too. This is off by default, because
     * it takes a relatively long time, so turn this on only if you know that part of your
     * model is located inside dependencies.
     */
    @Parameter(defaultValue = "false", property = "includeDependencies")
    private boolean includeDependencies;

    /**
     * When you include dependencies, we only look at compile and system scopes (by default)
     * You can change that here.
     * Valid options are: compile, provided, runtime, system, test, import
     */
    @Parameter(defaultValue = "compile,system", property = "includeDependenciesScopes")
    private List includeDependenciesScopes;

    /**
     * When you include dependencies, we only look at jars (by default)
     * You can change that here.
     */
    @Parameter(defaultValue = "jar", property = "includeDependenciesTypes")
    private List includeDependenciesTypes;

    @Parameter(defaultValue = "false", property = "includeScalars")
    private boolean includeScalars;

    @Parameter(defaultValue = "false", property = "includeDirectives")
    private boolean includeDirectives;

    @Parameter(defaultValue = "false", property = "includeSchemaDefinition")
    private boolean includeSchemaDefinition;

    @Parameter(defaultValue = "false", property = "includeIntrospectionTypes")
    private boolean includeIntrospectionTypes;

    @Parameter(defaultValue = "Default", property = "typeAutoNameStrategy")
    private String typeAutoNameStrategy;

    @Parameter(defaultValue = "${project}", required = true)
    private MavenProject mavenProject;

    @Parameter(property = "project.compileClasspathElements", required = true, readonly = true)
    private List classpath;

    @Parameter(defaultValue = "false", property = "skip")
    private boolean skip;

    /**
     * Compiled classes of the project.
     */
    @Parameter(defaultValue = "${project.build.outputDirectory}", property = "classesDir")
    private File classesDir;

    /**
     * @return the configuration set by the plugin's parameters
     */
    public static MavenConfig getMavenConfig() {
        return mavenConfig;
    }

    @Override
    public void execute() throws MojoExecutionException {
        mavenConfig = new MavenConfig(includeScalars, includeDirectives, includeSchemaDefinition, includeIntrospectionTypes,
                TypeAutoNameStrategy.valueOf(typeAutoNameStrategy));
        if (!skip) {
            ClassLoader classLoader = getClassLoader();
            Thread.currentThread().setContextClassLoader(classLoader);

            IndexView index = createIndex();
            String schema = generateSchema(index);
            if (schema != null) {
                write(schema);
            } else {
                getLog().warn("No Schema generated. Check that your code contains the MicroProfile GraphQL Annotations");
            }
        }
    }

    private IndexView createIndex() throws MojoExecutionException {
        List indexes = new ArrayList<>();
        try {
            IndexView moduleIndex = indexModuleClasses();
            indexes.add(moduleIndex);
        } catch (IOException e) {
            throw new MojoExecutionException("Can't compute index", e);
        }

        // always include Mutiny if it is present in the dependencies,
        // even if includeDependencies=false
        Predicate isMutiny = a -> a.getGroupId().equals("io.smallrye.reactive") &&
                a.getArtifactId().equals("mutiny");
        mavenProject.getArtifacts()
                .stream()
                .filter(isMutiny)
                .findAny()
                .ifPresent(a -> {
                    Result r = indexJar(((Artifact) a).getFile());
                    if (r != null) {
                        indexes.add(r.getIndex());
                    }
                });

        if (includeDependencies) {
            for (Object a : mavenProject.getArtifacts()) {
                Artifact artifact = (Artifact) a;
                if (includeDependenciesScopes.contains(artifact.getScope())
                        && includeDependenciesTypes.contains(artifact.getType())
                        && !isMutiny.test(artifact)) {
                    Result result = indexJar(artifact.getFile());
                    if (result != null) {
                        indexes.add(result.getIndex());
                    }
                }
            }
        }
        return CompositeIndex.create(indexes);
    }

    private Result indexJar(File file) {
        try {
            getLog().debug("Indexing file " + file);
            return JarIndexer.createJarIndex(file, new Indexer(),
                    false, false, false);
        } catch (IOException e) {
            getLog().error("Can't compute index of " + file.getAbsolutePath() + ", skipping", e);
            return null;
        }
    }

    // index the classes of this Maven module
    private Index indexModuleClasses() throws IOException {
        Indexer indexer = new Indexer();
        try (Stream classesDirStream = Files.walk(classesDir.toPath())) {
            List classFiles = classesDirStream
                    .filter(path -> path.toString().endsWith(".class"))
                    .collect(Collectors.toList());
            for (Path path : classFiles) {
                indexer.index(Files.newInputStream(path));
            }
        }
        return indexer.complete();
    }

    private String generateSchema(IndexView index) {
        Schema internalSchema = SchemaBuilder.build(index, mavenConfig.typeAutoNameStrategy);
        GraphQLSchema graphQLSchema = Bootstrap.bootstrap(internalSchema, true);
        if (graphQLSchema != null) {
            return new SchemaPrinter().print(graphQLSchema);
        }
        return null;
    }

    private void write(String schema) throws MojoExecutionException {
        try {
            if (destination == null || destination.isEmpty()) {
                // no destination file specified => print to stdout
                getLog().info(schema);
            } else {
                Path path = new File(destination).toPath();
                path.toFile().getParentFile().mkdirs();
                Files.write(path, schema.getBytes(),
                        StandardOpenOption.WRITE,
                        StandardOpenOption.CREATE,
                        StandardOpenOption.TRUNCATE_EXISTING);
                getLog().info("Wrote the schema to " + path.toAbsolutePath().toString());
            }
        } catch (IOException e) {
            throw new MojoExecutionException("Can't write the result", e);
        }
    }

    private ClassLoader getClassLoader() {
        Set urls = new HashSet<>();

        for (String element : classpath) {
            try {
                urls.add(new File(element).toURI().toURL());
            } catch (MalformedURLException ex) {
                throw new RuntimeException(ex);
            }
        }

        return URLClassLoader.newInstance(
                urls.toArray(new URL[0]),
                Thread.currentThread().getContextClassLoader());

    }

    public static class MavenConfig {
        private final boolean includeScalars;
        private final boolean includeDirectives;
        private final boolean includeSchemaDefinition;
        private final boolean includeIntrospectionTypes;
        private final TypeAutoNameStrategy typeAutoNameStrategy;

        public MavenConfig(boolean includeScalars, boolean includeDirectives, boolean includeSchemaDefinition,
                boolean includeIntrospectionTypes, TypeAutoNameStrategy typeAutoNameStrategy) {
            this.includeScalars = includeScalars;
            this.includeDirectives = includeDirectives;
            this.includeSchemaDefinition = includeSchemaDefinition;
            this.includeIntrospectionTypes = includeIntrospectionTypes;
            this.typeAutoNameStrategy = typeAutoNameStrategy;
        }

        public boolean isIncludeScalars() {
            return includeScalars;
        }

        public boolean isIncludeDirectives() {
            return includeDirectives;
        }

        public boolean isIncludeSchemaDefinition() {
            return includeSchemaDefinition;
        }

        public boolean isIncludeIntrospectionTypes() {
            return includeIntrospectionTypes;
        }

        public TypeAutoNameStrategy getTypeAutoNameStrategy() {
            return typeAutoNameStrategy;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy