Skip to content

Latest commit

 

History

History
377 lines (287 loc) · 16.2 KB

README.md

File metadata and controls

377 lines (287 loc) · 16.2 KB

License Gradle Plugin Portal Coverage Status build CodeQL OpenSSF Scorecard OpenSSF Best Practices

Creek JSON Schema Gradle Plugin

A Gradle plugin for generating JSON schemas from code using the Creel JSON Schema Generator.

See CreekService.org for info on Creek Service.

-## Supported Gradle versions

Gradle Version Tested version Notes
< 6.4 Not compatible due to API changes in Gradle
6.4.+ 6.4 Supported & tested
6.4+ 6.9.4 Supported & tested
7.+ 7.6.1 Supported & tested
8.+ 8.8 Supported & tested
> 8.8 Not currently tested. Should work...

// Todo: update Gradle matrix

Usage

The plugin is available on the Gradle Plugin Portal. See the portal for instructions on how to add the plugin to your build.

Tasks

The JSON Schema plugin adds the following tasks to your project:

generateJsonSchema - GenerateJsonSchema

NOTE

Details of how to annotate classes to control their schema can be found in the Creek JSON Schema Generator docs.

NOTE

Restricting class & module path scanning by setting allowed modules and allowed packages can increase the speed of your build.

Dependencies: compileJava, compileKotlin, compileGroovy if they exist. Dependants: processResources

The generateJsonSchema task searches the class and module path for with @GeneratesSchema and write out JSON schemas for them in YAML. The generated schema output directory is added to the main source set, as an additional resource directories, meaning the schema will be included in any generated jar.

Types can be annotated both with Jackson and JsonSchema annotations, allowing control of the generated schema.

See the Creel JSON Schema Generator Docs for more information and examples.

Aside from the customisations possible using the creek.schema.json extension, the task accepts the following command line options:

  • --type-scanning-allowed-module: (default: any module) restricts the classes to search to only those belonging to the specified module(s). Allowed module names can include the glob wildcard {@code *} character.
  • --type-scanning-allowed-package: (default: any package) restricts the classes to search to only those under the specified package(s). Allowed package names can include the glob wildcard {@code *} character.
  • --subtype-scanning-allowed-module: (default: any module) restricts the search for subtypes to only those belonging to the specified module(s). Allowed module names can include the glob wildcard {@code *} character.
  • --subtype-scanning-allowed-package: (default: any package) restrict the search for subtypes to only those under the specified package(s). Allowed package names can include the glob wildcard {@code *} character.

For example, the following limits the class & module path scanning to only two modules:

> ./gradlew generateJsonSchema \
    --type-scanning-allowed-module=acme.finance.momdel \
    --type-scanning-allowed-module=acme.sales.model

generateTestJsonSchema - GenerateJsonSchema

NOTE

Details of how to annotate classes to control their schema can be found in the Creek JSON Schema Generator docs.

NOTE

Restricting class & module path scanning by setting allowed modules and allowed packages can increase the speed of your build.

Dependencies: compileTestJava, compileTestKotlin, compileTestGroovy if they exist. Dependants: processTestResources

The generateTestJsonSchema works the same as generateJsonSchema, only for test code. The generated schema output directory is added to the test source set, as an additional resource directories, meaning the schema will be available during unit testing.

NOTE: due to a bug in the org.javamodularity.moduleplugin Gradle plugin, generated resources are NOT patched in to the module during unit testing. This should be fixed once java9-modularity/gradle-modules-plugin#228 is merged and released.

cleanTaskName - Delete

Deletes the files created by the specified task. For example, cleanGenerateJsonSchema will delete the generated JSON schema files.

Project Layout

The JSON Schema plugin does not require any specific project layout.

Dependency Management

The JSON Schema plugin adds a number of dependency configurations to your project. Tasks such as generateJsonSchema then use these configurations to get the corresponding files and use them, for example by adding them to the class path when generating schemas.

  • jsonSchemaGenerator the JSON schema generator dependency, defaulting to the same version as the plugin.

Changing the JSON schema generator version

By default, the plugin generates JSON schema using the JSON schema generator of the same version. However, you can configure the generator version via the jsonSchemaGenerator dependency configuration.

For example, the following would always use the latest available generator:

Groovy: Custom JSON Schema executor version
dependencies {
    jsonSchemaGenerator 'org.creekservice:creek-json-schema-generator:+'
}
Kotlin: Custom JSON Schema executor version
dependencies {
    jsonSchemaGenerator("org.creekservice:creek-json-schema-generator:+")
}

When running a different version of the generator it may be that the generator supports command line options that are not exposed by the plugin. In such situations, you can pass extra arguments to the generator using the extraArguments method of the creek.schema.json extension.

Groovy: Passing extra arguments to the generator
creek {
    schema {
        json {
            extraArguments "--some", "--extra=arguments"
        }
    }
}
Kotlin: Passing extra arguments to the generator
jsonSchema {
    schema {
        json {
            extraArguments("--some", "--extra=arguments")    
        }
    }
}

JSON Schema Extension

The JSON Schema plugin adds a json extension to creek.schema. This allows you to configure a number of task related properties inside a dedicated DSL block.

Groovy: Using the creek.schema.json extension
creek.schema.json {
    
    // Configure scanning for @GeneratesSchema annotated types:
    typeScanning {
        // Restrict scanning to certain JPMS modules:
        moduleWhiteList 'acme-finance', 'acme-sales-*'

        // Restrict scanning to certain packages:
        packageWhiteList 'com.acme.finance', 'com.acme.sales.*'
    }

    // Configure scanning subtypes:
    subTypeScanning {
        // Restrict scanning to certain JPMS modules:
        moduleWhiteList 'acme-finance', 'acme-sales-*'

        // Restrict class scanning for subtypes to certain packages:
        packageWhiteList 'com.acme.finance', 'com.acme.sales.*'
    }
}
Kotlin: Using the creek.schema.json extension
creek.schema.json {
    // Configure scanning for @GeneratesSchema annotated types:
    typeScanning {
        // Restrict class scanning to certain JPMS modules:
        moduleWhiteList("acme-finance", "acme-sales-")

        // Restrict class scanning for base types to certain packages:
        packageWhiteList("com.acme.finance", "com.acme.sales.*")
    }

    // Configure scanning subtypes:
    subTypeScanning {
        // Restrict class scanning to certain JPMS modules:
        moduleWhiteList("acme-finance", "acme-sales-")

        // Restrict class scanning for subtypes to certain packages:
        packageWhiteList("com.acme.finance", "com.acme.sales.*")
    }
}

// Todo: discuyss module plugin and $moduleName

Speeding up class scanning

The generator scans the class and module paths of the JVM to find:

  • base types to generate a schema for, i.e. types annotated with @GeneratesSchema.
  • subtypes of polymorphic types it encounters while building schema, where the base type does not define an explicit set of subtypes, i.e. types annotated with @JsonTypeInfo, but not @JsonSubTypes.

By default, these scans scan the entire class and module paths of the JVM. This can both slow down your build and result in unwanted schema files being generated for annotated types found in dependencies.

The path scans can be restricted to exclude unwanted types from schema generation and speed up the build.

Controlling which types generate schemas

Base type scanning, i.e. scanning for types annotated with @GeneratesSchema, can be restricted by either supplying one or more JPMS modules to scan:

Groovy: Restricting modules to scan for @GeneratesSchema annotated types
creek.schema.json {
    typeScanning {
        moduleWhiteList 'acme-finance', 'acme-sales-*'
    }
}
Kotlin: Restricting modules to scan for @GeneratesSchema annotated types
creek.schema.json {
    typeScanning {
        moduleWhiteList("acme-finance", "acme-sales")
    }
}

...and/or by restricting which package names to search under:

Groovy: Restricting packages to scan for @GeneratesSchema annotated types
creek.schema.json {
    typeScanning {
        packageWhiteList 'com.acme.finance', 'com.acme.sales.*'
    }
}
Kotlin: Restricting packages to scan for @GeneratesSchema annotated types
creek.schema.json {
    typeScanning {
        packageWhiteList("com.acme.finance", "com.acme.sales.*")
    }
}

Controlling which subtypes are included in schemas

Subtype scanning, i.e. scanning for subtypes of polymorphic types, can be restricted by restricting which package names to search under:

Groovy: Restricting modules to scan for subtypes
creek.schema.json {
    subTypeScanning {
        moduleWhiteList("acme-finance", "acme-sales")
        packageWhiteList 'com.acme.finance', 'com.acme.sales.*'
    }
}
Kotlin: Restricting modules to scan for subtypes
creek.schema.json {
    subTypeScanning {
        moduleWhiteList("acme-finance", "acme-sales")
        packageWhiteList("com.acme.finance", "com.acme.sales.*")
    }
}

JSON Schema Generation

The generateJsonSchema task generates YAML files containing the JSON schema of each @GeneratesSchema annotated type it encounters. By default, these are written to $buildDir/generated/resources/schema/main/schema/json, with $buildDir/generated/resources/schema/main being added as a resource root. This means the schema files generated will be included in the jar under a schema/json directory.

The generateTestJsonSchema task outputs to the $buildDir/generated/resources/schema/test/schema/json, with $buildDir/generated/resources/schema/test being added as a resource root.

Changing schema file location

The location where schema files are written can be changed by changing either:

  • creek.schema.json.schemaResourceRoot: the resource root for schema, defaulting to $buildDir/generated/resources/schema/main, or
  • creek.schema.json.testSchemaResourceRoot: the resource root for test schema, defaulting to $buildDir/generated/resources/schema/test, or
  • creek.schema.json.outputDirectoryName: the name of the subdirectory under creek.schema.json.schemaResourceRoot and creek.schema.json.testSchemaResourceRoot where schemas will be written, and defining the relative path to the schema files within the resulting jar file. This defaults to not being set, which causes the plugin to outputting schemas under a package directory structure. If this property is set, schemas will be generated into the specified flat directory, where the filename matches that full class name.
Groovy: Customising schema file output location
creek.schema.json {
  schemaResourceRoot = file("$buildDir/custom/build/path")
  testSchemaResourceRoot = file("$buildDir/custom/build/path/for/test/schema")
  outputDirectoryName = "custom/path/within/jar"
}
Kotlin: Customising schema file output location
creek.schema.json {
    schemaResourceRoot.set(file("$buildDir/custom/build/path"))
    testSchemaResourceRoot.set(file("$buildDir/custom/build/path/for/test/schema"))
    outputDirectoryName.set("custom/path/within/jar")
}

Resources in Java Modules

Java 9 introduced the JPMS which, by default, encapsulates classes and resources. Resources, for example generated schema files, that are not in the root of jar, are encapsulated by default, i.e. not accessible from outside the module, unless the module definition explicitly opens the package the resource is in.

Additionally, modules require each module to expose unique packages.

With default configuration this plugin will generate schemas under a resource root using the same directory structure as the source types, i.e. the schema file will be in the same package as the type it was built from in the jar file.

If you wish the generated schema resources to be accessible from other modules, then the module definition must include an opens statement for the package containing the source types.

For example, given types that generate schemas in an acme.finance.model package, ensure:

module acme.model {
    // Export the package types to everyone, i.e. the code:
    exports acme.finance.model;

    // Open the package resources to everyone, i.e. the schema files:
    opens acme.finance.model;
}

JVM Language support

Currently, the plugin automatically configures tasks to work with the standard Java, Groovy and Kotlin plugins. Schema generation tasks are configured to read the output of compile[Test]Java, compile[Test]Groovy and compile[Test]Kotlin tasks if they are present in the project.

Support for other JVM languages may be added later, or you may be able to configure your own instance of GenerateJsonSchema to work with your chosen language. (Consider adding details to Issue 6 if you do).