GraphQLCodegenPlugin.scala 15.5 KB
Newer Older
梦境迷离's avatar
梦境迷离 已提交
1 2 3 4 5
package io.github.dreamylost.graphql.codegen

import java.nio.file.{ Path, Paths }
import java.util

6 7
import com.kobylynskyi.graphql.codegen.GraphQLCodegenValidate
import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen
梦境迷离's avatar
梦境迷离 已提交
8
import com.kobylynskyi.graphql.codegen.model._
9 10 11
import com.kobylynskyi.graphql.codegen.model.exception.LanguageNotSupportedException
import com.kobylynskyi.graphql.codegen.model.GeneratedLanguage._
import com.kobylynskyi.graphql.codegen.scala.ScalaGraphQLCodegen
梦境迷离's avatar
梦境迷离 已提交
12 13 14 15 16 17 18 19 20 21 22 23
import com.kobylynskyi.graphql.codegen.supplier.{ JsonMappingConfigSupplier, SchemaFinder }
import sbt.{ AutoPlugin, Def, PluginTrigger, _ }
import sbt.Keys.{ sLog, sourceManaged, _ }
import sbt.internal.util.complete.DefaultParsers.spaceDelimited

import scala.collection.JavaConverters._

/**
 *
 * @author liguobin@growingio.com
 * @version 1.0,2020/7/15
 */
24
object GraphQLCodegenPlugin extends GraphQLCodegenPlugin(Compile, configurationPostfix = "-main") {
梦境迷离's avatar
梦境迷离 已提交
25 26 27 28
  //for auto import
  val autoImport = GlobalImport
}

29 30
class GraphQLCodegenPlugin(configuration: Configuration, private[codegen] val configurationPostfix: String = "") extends AutoPlugin with Compat {
  self =>
梦境迷离's avatar
梦境迷离 已提交
31 32

  //override this by graphqlJavaCodegenVersion and javaxValidationApiVersion
33 34
  private val jValidation = BuildInfo.jValidationVersion
  private val codegen = BuildInfo.version
梦境迷离's avatar
梦境迷离 已提交
35 36 37 38 39 40 41 42 43 44 45

  object GlobalImport extends GraphQLCodegenKeys {

    //should look for a way to automatically add to the classpath
    lazy val GraphQLCodegenPluginDependencies: Def.Setting[Seq[ModuleID]] = libraryDependencies ++= Seq(
      "javax.validation" % "validation-api" % javaxValidationApiVersion.value.getOrElse(jValidation),
      "io.github.kobylynskyi" % "graphql-java-codegen" % graphqlJavaCodegenVersion.value.getOrElse(codegen)
    )

    lazy val schemaFinderConfig: SchemaFinderConfig = SchemaFinderConfig(null)
    lazy val parentInterfacesConfig: ParentInterfacesConfig = ParentInterfacesConfig()
46
    lazy val defaultRelayConfig = new RelayConfig() //for auto import which can change it by `set` methods.
47 48
    lazy val GraphQLCodegenConfig = self.GraphQLCodegenConfig

梦境迷离's avatar
梦境迷离 已提交
49 50 51 52 53 54 55 56
  }

  //no Auto trigger
  //Eventually I decided not to use auto trigger
  override def trigger: PluginTrigger = noTrigger

  override def requires = sbt.plugins.JvmPlugin

57 58
  override def projectConfigurations: Seq[Configuration] = GraphQLCodegenConfig :: Nil

梦境迷离's avatar
梦境迷离 已提交
59 60 61 62
  import GlobalImport._

  //With the implementation of some other plugins, initialization is not necessary,
  //but maybe should be related to the dependency of key. For convenience, this is a conservative operation
63
  override lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
64
    generatedLanguage := MappingConfigConstants.DEFAULT_GENERATED_LANGUAGE,
65
    graphqlQueryIntrospectionResultPath := None,
梦境迷离's avatar
梦境迷离 已提交
66 67 68 69
    graphqlSchemas := schemaFinderConfig,
    jsonConfigurationFile := None,
    graphqlSchemaPaths := Seq.empty,
    graphqlSchemaValidate := Seq.empty,
70
    customTypesMapping := new util.HashMap[String, String](), //TODO use scala Map, convert to java Map
71 72
    customAnnotationsMapping := new util.HashMap[String, util.List[String]](),
    directiveAnnotationsMapping := new util.HashMap[String, util.List[String]](),
梦境迷离's avatar
梦境迷离 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85
    javaxValidationApiVersion := None,
    graphqlJavaCodegenVersion := None,
    // suffix/prefix/strategies:
    apiNamePrefix := None,
    apiNameSuffix := MappingConfigConstants.DEFAULT_RESOLVER_SUFFIX,
    apiRootInterfaceStrategy := ApiRootInterfaceStrategy.valueOf(MappingConfigConstants.DEFAULT_API_ROOT_INTERFACE_STRATEGY_STRING),
    apiNamePrefixStrategy := ApiNamePrefixStrategy.valueOf(MappingConfigConstants.DEFAULT_API_NAME_PREFIX_STRATEGY_STRING),
    modelNamePrefix := None,
    modelNameSuffix := None,
    requestSuffix := MappingConfigConstants.DEFAULT_REQUEST_SUFFIX,
    responseSuffix := MappingConfigConstants.DEFAULT_RESPONSE_SUFFIX,
    responseProjectionSuffix := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_SUFFIX,
    parametrizedInputSuffix := MappingConfigConstants.DEFAULT_PARAMETRIZED_INPUT_SUFFIX,
86
    useObjectMapperForRequestSerialization := new util.HashSet[String](),
梦境迷离's avatar
梦境迷离 已提交
87 88 89 90
    typeResolverPrefix := None,
    typeResolverSuffix := MappingConfigConstants.DEFAULT_RESOLVER_SUFFIX,
    subscriptionReturnType := None,
    modelValidationAnnotation := MappingConfigConstants.DEFAULT_VALIDATION_ANNOTATION,
91 92
    apiReturnType := None,
    apiReturnListType := None,
93 94
    apiInterfaceStrategy := MappingConfigConstants.DEFAULT_API_INTERFACE_STRATEGY,
    useOptionalForNullableReturnTypes := MappingConfigConstants.DEFAULT_USE_OPTIONAL_FOR_NULLABLE_RETURN_TYPES,
95
    generateApisWithThrowsException := MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION,
96
    addGeneratedAnnotation := MappingConfigConstants.DEFAULT_ADD_GENERATED_ANNOTATION,
97
    relayConfig := defaultRelayConfig,
梦境迷离's avatar
梦境迷离 已提交
98 99 100 101 102 103 104
    // package name configs:
    apiPackageName := None,
    modelPackageName := None,
    // field resolvers configs:
    fieldsWithResolvers := new util.HashSet[String](),
    fieldsWithoutResolvers := new util.HashSet[String](),
    // various toggles:
105 106 107 108 109
    generateClient := MappingConfigConstants.DEFAULT_GENERATE_CLIENT,
    generateParameterizedFieldsResolvers := MappingConfigConstants.DEFAULT_GENERATE_PARAMETERIZED_FIELDS_RESOLVERS,
    generateExtensionFieldsResolvers := MappingConfigConstants.DEFAULT_GENERATE_EXTENSION_FIELDS_RESOLVERS,
    generateDataFetchingEnvironmentArgumentInApis := MappingConfigConstants.DEFAULT_GENERATE_DATA_FETCHING_ENV,
    generateModelsForRootTypes := MappingConfigConstants.DEFAULT_GENERATE_MODELS_FOR_ROOT_TYPES,
梦境迷离's avatar
梦境迷离 已提交
110
    generatePackageName := None,
111 112 113 114 115
    generateBuilder := MappingConfigConstants.DEFAULT_BUILDER,
    generateApis := MappingConfigConstants.DEFAULT_GENERATE_APIS,
    generateEqualsAndHashCode := MappingConfigConstants.DEFAULT_EQUALS_AND_HASHCODE,
    generateImmutableModels := MappingConfigConstants.DEFAULT_GENERATE_IMMUTABLE_MODELS,
    generateToString := MappingConfigConstants.DEFAULT_TO_STRING,
梦境迷离's avatar
梦境迷离 已提交
116
    // parent interfaces configs:
117 118
    parentInterfaces := parentInterfacesConfig,
    responseProjectionMaxDepth := MappingConfigConstants.DEFAULT_RESPONSE_PROJECTION_MAX_DEPTH
梦境迷离's avatar
梦境迷离 已提交
119 120 121 122 123
  )

  private def getMappingConfig(): Def.Initialize[MappingConfig] = Def.setting[MappingConfig] {

    val mappingConfig = new MappingConfig
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    mappingConfig.setPackageName((generatePackageName in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setCustomTypesMapping((customTypesMapping in GraphQLCodegenConfig).value)
    mappingConfig.setApiNameSuffix((apiNameSuffix in GraphQLCodegenConfig).value)
    mappingConfig.setApiNamePrefix((apiNamePrefix in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setApiRootInterfaceStrategy((apiRootInterfaceStrategy in GraphQLCodegenConfig).value)
    mappingConfig.setApiNamePrefixStrategy((apiNamePrefixStrategy in GraphQLCodegenConfig).value)
    mappingConfig.setModelNamePrefix((modelNamePrefix in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setModelNameSuffix((modelNameSuffix in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setApiPackageName((apiPackageName in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setModelPackageName((modelPackageName in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setGenerateBuilder((generateBuilder in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateApis((generateApis in GraphQLCodegenConfig).value)
    mappingConfig.setTypeResolverSuffix((typeResolverSuffix in GraphQLCodegenConfig).value)
    mappingConfig.setTypeResolverPrefix((typeResolverPrefix in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setModelValidationAnnotation((modelValidationAnnotation in GraphQLCodegenConfig).value)
    mappingConfig.setCustomAnnotationsMapping((customAnnotationsMapping in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateEqualsAndHashCode((generateEqualsAndHashCode in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateImmutableModels((generateImmutableModels in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateToString((generateToString in GraphQLCodegenConfig).value)
    mappingConfig.setSubscriptionReturnType((subscriptionReturnType in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setGenerateParameterizedFieldsResolvers((generateParameterizedFieldsResolvers in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateDataFetchingEnvironmentArgumentInApis((generateDataFetchingEnvironmentArgumentInApis in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateExtensionFieldsResolvers((generateExtensionFieldsResolvers in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateModelsForRootTypes((generateModelsForRootTypes in GraphQLCodegenConfig).value)
    mappingConfig.setFieldsWithResolvers((fieldsWithResolvers in GraphQLCodegenConfig).value)
    mappingConfig.setFieldsWithoutResolvers((fieldsWithoutResolvers in GraphQLCodegenConfig).value)
    mappingConfig.setGenerateClient((generateClient in GraphQLCodegenConfig).value)
    mappingConfig.setRequestSuffix((requestSuffix in GraphQLCodegenConfig).value)
    mappingConfig.setResponseSuffix((responseSuffix in GraphQLCodegenConfig).value)
    mappingConfig.setResponseProjectionSuffix((responseProjectionSuffix in GraphQLCodegenConfig).value)
    mappingConfig.setParametrizedInputSuffix((parametrizedInputSuffix in GraphQLCodegenConfig).value)
155
    mappingConfig.setUseObjectMapperForRequestSerialization((useObjectMapperForRequestSerialization in GraphQLCodegenConfig).value)
156 157 158 159
    mappingConfig.setResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.resolver)
    mappingConfig.setQueryResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.queryResolver)
    mappingConfig.setMutationResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.mutationResolver)
    mappingConfig.setSubscriptionResolverParentInterface((parentInterfaces in GraphQLCodegenConfig).value.subscriptionResolver)
160 161
    mappingConfig.setApiReturnType((apiReturnType in GraphQLCodegenConfig).value.orNull)
    mappingConfig.setApiReturnListType((apiReturnListType in GraphQLCodegenConfig).value.orNull)
162
    mappingConfig.setDirectiveAnnotationsMapping((directiveAnnotationsMapping in GraphQLCodegenConfig).value)
163 164
    mappingConfig.setApiInterfaceStrategy((apiInterfaceStrategy in GraphQLCodegenConfig).value)
    mappingConfig.setUseOptionalForNullableReturnTypes((useOptionalForNullableReturnTypes in GraphQLCodegenConfig).value)
165
    mappingConfig.setGenerateApisWithThrowsException((generateApisWithThrowsException in GraphQLCodegenConfig).value)
166
    mappingConfig.setAddGeneratedAnnotation((addGeneratedAnnotation in GraphQLCodegenConfig).value)
167
    mappingConfig.setResponseProjectionMaxDepth((responseProjectionMaxDepth in GraphQLCodegenConfig).value)
168
    mappingConfig.setRelayConfig((relayConfig in GraphQLCodegenConfig).value)
169
    mappingConfig.setGeneratedLanguage((generatedLanguage in GraphQLCodegenConfig).value)
170
    sLog.value.info(s"Version is <${BuildInfo.toString}>")
梦境迷离's avatar
梦境迷离 已提交
171 172 173
    mappingConfig
  }

174
  override lazy val projectSettings: Seq[Def.Setting[_]] = inConfig(GraphQLCodegenConfig) {
梦境迷离's avatar
梦境迷离 已提交
175
    Seq(
176 177 178 179 180 181 182
      generateCodegenTargetPath := crossTarget.value / "src_managed_graphql",
      sourceManaged := (generateCodegenTargetPath in GraphQLCodegenConfig).value,
      javaSource in configuration := (sourceManaged in GraphQLCodegenConfig).value,
      managedSourceDirectories in configuration ++= Seq((sourceManaged in GraphQLCodegenConfig).value),
      managedClasspath := {
        Classpaths.managedJars(GraphQLCodegenConfig, (classpathTypes in GraphQLCodegenConfig).value, (update in GraphQLCodegenConfig).value)
      },
梦境迷离's avatar
梦境迷离 已提交
183
      outputDir := {
184
        val file = (javaSource in configuration).value
梦境迷离's avatar
梦境迷离 已提交
185 186 187 188 189
        if (!file.exists()) {
          file.mkdirs()
        }
        sLog.value.info(s"Default outputDir is <${file.getAbsolutePath}>")
        file
190 191 192 193
      }, //use validate that config in build.sbt
      graphqlCodegenValidate := {
        val schemas = if ((graphqlSchemaPaths in GraphQLCodegenConfig).value.isEmpty) {
          Seq(((resourceDirectory in configuration).value / "schema.graphql").getCanonicalPath).asJava
梦境迷离's avatar
梦境迷离 已提交
194
        } else {
195
          (graphqlSchemaPaths in GraphQLCodegenConfig).value.asJava
梦境迷离's avatar
梦境迷离 已提交
196 197
        }
        new GraphQLCodegenValidate(schemas).validate() //use validate at terminal by user
198 199
      },
      graphqlSchemaValidate := {
梦境迷离's avatar
梦境迷离 已提交
200 201 202 203 204 205
        //use by user
        val args: Seq[String] = spaceDelimited("<arg>").parsed
        new GraphQLCodegenValidate(args.asJava).validate()
        args.foreach(a  sLog.value.info(s"Obtain args <$a>"))
        args
      }, graphqlCodegen := {
206
        val mappingConfigSupplier: JsonMappingConfigSupplier = buildJsonSupplier((jsonConfigurationFile in GraphQLCodegenConfig).value.orNull)
梦境迷离's avatar
梦境迷离 已提交
207 208
        var result: Seq[File] = Seq.empty
        try {
209 210 211 212 213 214 215 216 217 218 219 220 221
          val _outputDir = (outputDir in GraphQLCodegenConfig).value
          val _introspectionResult = (graphqlQueryIntrospectionResultPath in GraphQLCodegenConfig).value.orNull
          lazy val instantiateCodegen = (mappingConfig: MappingConfig) => {
            (generatedLanguage in GraphQLCodegenConfig).value match {
              case JAVA =>
                new JavaGraphQLCodegen(getSchemas, _introspectionResult, _outputDir, mappingConfig, mappingConfigSupplier)
              case SCALA =>
                new ScalaGraphQLCodegen(getSchemas, _introspectionResult, _outputDir, mappingConfig, mappingConfigSupplier)
              case _ =>
                throw new LanguageNotSupportedException((generatedLanguage in GraphQLCodegenConfig).value)
            }
          }
          result = instantiateCodegen(getMappingConfig().value).generate.asScala
梦境迷离's avatar
梦境迷离 已提交
222
          for (file  result) {
223
            sLog.value.success(s"${file.getName}")
梦境迷离's avatar
梦境迷离 已提交
224 225 226
          }
        } catch {
          case e: Exception 
227
            throw new Exception(s"${e.getLocalizedMessage}")
梦境迷离's avatar
梦境迷离 已提交
228 229 230
        }

        def getSchemas: util.List[String] = {
231 232
          if ((graphqlSchemaPaths in GraphQLCodegenConfig).value != null && (graphqlSchemaPaths in GraphQLCodegenConfig).value.nonEmpty)
            return (graphqlSchemaPaths in GraphQLCodegenConfig).value.asJava
梦境迷离's avatar
梦境迷离 已提交
233 234
          val schemasRootDir: Path = getSchemasRootDir
          val finder: SchemaFinder = new SchemaFinder(schemasRootDir)
235 236 237
          finder.setRecursive((graphqlSchemas in GraphQLCodegenConfig).value.recursive)
          finder.setIncludePattern((graphqlSchemas in GraphQLCodegenConfig).value.includePattern)
          finder.setExcludedFiles((graphqlSchemas in GraphQLCodegenConfig).value.excludedFiles.asJava)
梦境迷离's avatar
梦境迷离 已提交
238 239 240 241
          finder.findSchemas
        }

        def getSchemasRootDir: Path = {
242
          val rootDir = (graphqlSchemas in GraphQLCodegenConfig).value.rootDir
梦境迷离's avatar
梦境迷离 已提交
243 244 245 246 247 248 249 250 251
          if (rootDir == null) {
            val default = getDefaultResourcesDirectory
            if (default == null) throw new IllegalStateException("Default resource folder not found, please provide <rootDir> in <graphqlSchemas>")
            else return default
          }
          Paths.get(rootDir)
        }

        def getDefaultResourcesDirectory: Path = {
252
          val file = (resourceDirectory in configuration).value
梦境迷离's avatar
梦境迷离 已提交
253 254 255 256 257 258 259 260 261 262 263
          if (!file.exists()) {
            file.mkdirs()
          }
          val path = Paths.get(file.getPath)
          sLog.value.info(s"Default resources path <$path>")
          path
        }

        result
      }
    //watch graphql schema source
264
    ) ++ watchSourcesSetting ++ Seq(cleanFiles += (generateCodegenTargetPath in GraphQLCodegenConfig).value)
梦境迷离's avatar
梦境迷离 已提交
265 266 267 268 269 270 271
  }

  private def buildJsonSupplier(jsonConfigurationFile: String): JsonMappingConfigSupplier = {
    if (jsonConfigurationFile != null && jsonConfigurationFile.nonEmpty) new JsonMappingConfigSupplier(jsonConfigurationFile) else null
  }

}