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

com.expediagroup.graphql.plugin.schema.GenerateSDL.kt Maven / Gradle / Ivy

/*
 * Copyright 2022 Expedia, Inc
 *
 * 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
 *
 *     https://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.
 */

package com.expediagroup.graphql.plugin.schema

import com.expediagroup.graphql.generator.SchemaGeneratorConfig
import com.expediagroup.graphql.generator.TopLevelObject
import com.expediagroup.graphql.generator.extensions.print
import com.expediagroup.graphql.generator.federation.FederatedSchemaGeneratorConfig
import com.expediagroup.graphql.generator.federation.FederatedSchemaGeneratorHooks
import com.expediagroup.graphql.generator.federation.toFederatedSchema
import com.expediagroup.graphql.generator.hooks.NoopSchemaGeneratorHooks
import com.expediagroup.graphql.generator.toSchema
import com.expediagroup.graphql.plugin.schema.hooks.SchemaGeneratorHooksProvider
import com.expediagroup.graphql.server.Schema
import com.expediagroup.graphql.server.operations.Mutation
import com.expediagroup.graphql.server.operations.Query
import com.expediagroup.graphql.server.operations.Subscription
import io.github.classgraph.ClassGraph
import io.github.classgraph.ScanResult
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.ServiceLoader

private val logger: Logger = LoggerFactory.getLogger("generateSDL")

/**
 * Generates GraphQL schema in SDL format.
 *
 * Relies on a `ServiceLoader` mechanism to dynamically load an instance of a `SchemaGeneratorHooksProvider` from the classpath.
 */
fun generateSDL(supportedPackages: List): String {
    val hooksProviders = ServiceLoader.load(SchemaGeneratorHooksProvider::class.java).toList()
    val hooks = when {
        hooksProviders.isEmpty() -> {
            logger.warn("No SchemaGeneratorHooksProvider were found, defaulting to NoopSchemaGeneratorHooks")
            NoopSchemaGeneratorHooks
        }
        hooksProviders.size > 1 -> {
            throw RuntimeException("Cannot generate SDL as multiple SchemaGeneratorHooksProviders were found on the classpath")
        }
        else -> {
            val provider = hooksProviders.first()
            logger.debug("SchemaGeneratorHooksProvider found, ${provider.javaClass.simpleName} will be used to generate the hooks")
            provider.hooks()
        }
    }
    val scanResult = ClassGraph()
        .enableAllInfo()
        .acceptPackages(*supportedPackages.toTypedArray())
        .scan()

    val queries = findTopLevelObjects(scanResult, Query::class.java)
    val mutations = findTopLevelObjects(scanResult, Mutation::class.java)
    val subscriptions = findTopLevelObjects(scanResult, Subscription::class.java)
    val schemaObject = findTopLevelObjects(scanResult, Schema::class.java).firstOrNull()

    // TODO support top level name overrides?
    val schema = if (hooks is FederatedSchemaGeneratorHooks) {
        logger.debug("Generating federated schema using hooks = ${hooks.javaClass.simpleName}")
        logger.debug("  query classes = ${queries.map { it.kClass }}")
        logger.debug("  mutation classes = ${mutations.map { it.kClass }}")
        logger.debug("  subscription classes = ${subscriptions.map { it.kClass }}")
        val config = FederatedSchemaGeneratorConfig(
            supportedPackages = supportedPackages,
            hooks = hooks
        )
        toFederatedSchema(
            config = config,
            queries = queries,
            mutations = mutations,
            subscriptions = subscriptions,
            schemaObject = schemaObject
        )
    } else {
        logger.debug("Generating schema using hooks = ${hooks.javaClass.simpleName}")
        logger.debug("  query classes = ${queries.map { it.kClass }}")
        logger.debug("  mutation classes = ${mutations.map { it.kClass }}")
        logger.debug("  subscription classes = ${subscriptions.map { it.kClass }}")
        val config = SchemaGeneratorConfig(
            supportedPackages = supportedPackages,
            hooks = hooks
        )
        toSchema(
            config = config,
            queries = queries,
            mutations = mutations,
            subscriptions = subscriptions,
            schemaObject = schemaObject
        )
    }

    scanResult.close()
    return schema.print()
}

private fun findTopLevelObjects(scanResult: ScanResult, markupClass: Class<*>): List =
    scanResult.getClassesImplementing(markupClass.name)
        .map { it.loadClass() }
        .map { TopLevelObject(null, it.kotlin) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy