From 22970ae118b21055a2ec5652805b0e72445bba3b Mon Sep 17 00:00:00 2001 From: Hunter Mellema Date: Tue, 19 Mar 2024 12:05:21 -0600 Subject: [PATCH 1/3] Add tutorial project for smithy gradle plugins --- .../integ/tutorial/build.gradle.kts | 25 ++++++++ .../examples/plugins/PluginTutorialTest.java | 58 +++++++++++++++++++ gradle-plugin-examples/tutorial/README.md | 26 +++++++++ .../tutorial/gradle/libs.versions.toml | 9 +++ .../tutorial/lib/build.gradle.kts | 45 ++++++++++++++ .../tutorial/lib/model/common.smithy | 23 ++++++++ .../tutorial/lib/smithy-build.json | 3 + .../com/example/cafe/DocUrlValidator.java | 35 +++++++++++ ...n.smithy.model.validation.ValidatorService | 2 + .../tutorial/service/build.gradle.kts | 15 +++++ .../tutorial/service/model/service.smithy | 58 +++++++++++++++++++ .../tutorial/service/smithy-build.json | 3 + .../tutorial/settings.gradle.kts | 15 +++++ settings.gradle.kts | 7 +++ smithy-templates.json | 13 +++++ 15 files changed, 337 insertions(+) create mode 100644 gradle-plugin-examples/integ/tutorial/build.gradle.kts create mode 100644 gradle-plugin-examples/integ/tutorial/src/test/java/io/smithy/examples/plugins/PluginTutorialTest.java create mode 100644 gradle-plugin-examples/tutorial/README.md create mode 100644 gradle-plugin-examples/tutorial/gradle/libs.versions.toml create mode 100644 gradle-plugin-examples/tutorial/lib/build.gradle.kts create mode 100644 gradle-plugin-examples/tutorial/lib/model/common.smithy create mode 100644 gradle-plugin-examples/tutorial/lib/smithy-build.json create mode 100644 gradle-plugin-examples/tutorial/lib/src/main/java/com/example/cafe/DocUrlValidator.java create mode 100644 gradle-plugin-examples/tutorial/lib/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.ValidatorService create mode 100644 gradle-plugin-examples/tutorial/service/build.gradle.kts create mode 100644 gradle-plugin-examples/tutorial/service/model/service.smithy create mode 100644 gradle-plugin-examples/tutorial/service/smithy-build.json create mode 100644 gradle-plugin-examples/tutorial/settings.gradle.kts diff --git a/gradle-plugin-examples/integ/tutorial/build.gradle.kts b/gradle-plugin-examples/integ/tutorial/build.gradle.kts new file mode 100644 index 0000000..9971c55 --- /dev/null +++ b/gradle-plugin-examples/integ/tutorial/build.gradle.kts @@ -0,0 +1,25 @@ + +plugins { + id("java-library") +} + +// The test project doesn't produce a JAR. +tasks["jar"].enabled = false + +tasks.named("test") { + useJUnitPlatform() + systemProperty("buildFolder", + gradle.includedBuild("tutorial").projectDir.resolve("service").resolve("build").absolutePath) +} + +tasks["test"].dependsOn(gradle.includedBuild("tutorial").task(":service:build")) + +dependencies { + val smithyVersion: String by project + + testImplementation("software.amazon.smithy:smithy-model:$smithyVersion") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.4.0") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.4.0") + testImplementation("org.hamcrest:hamcrest:2.1") +} diff --git a/gradle-plugin-examples/integ/tutorial/src/test/java/io/smithy/examples/plugins/PluginTutorialTest.java b/gradle-plugin-examples/integ/tutorial/src/test/java/io/smithy/examples/plugins/PluginTutorialTest.java new file mode 100644 index 0000000..4f84918 --- /dev/null +++ b/gradle-plugin-examples/integ/tutorial/src/test/java/io/smithy/examples/plugins/PluginTutorialTest.java @@ -0,0 +1,58 @@ +package io.smithy.examples.plugins; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.validation.Severity; +import software.amazon.smithy.model.validation.ValidationEvent; + +public class PluginTutorialTest { + + static File smithyBuildInfo; + static List validationEvents; + + @BeforeAll + static void setup() throws FileNotFoundException { + Path buildFolderPath = Paths.get(System.getProperty("buildFolder")); + System.out.println(buildFolderPath); + smithyBuildInfo = buildFolderPath.resolve("smithyprojections") + .resolve("service") + .resolve("source") + .resolve("build-info") + .resolve("smithy-build-info.json") + .toFile(); + var jsonOutput = Node.parse(new FileInputStream(smithyBuildInfo)).expectObjectNode(); + validationEvents = jsonOutput.expectMember("validationEvents") + .expectArrayNode() + .getElementsAs(ValidationEvent::fromNode); + } + + @Test + public void correctNumberOfValidations() { + var expectedMatches = 1; + assertEquals(validationEvents.size(), expectedMatches); + } + + @Test + public void correctSeverityOfValidations() { + assertTrue(validationEvents.stream() + .allMatch(event -> event.getSeverity().equals(Severity.WARNING)) + ); + } + + @Test + public void correctTypesOfValidations() { + assertEquals(validationEvents.stream() + .peek(System.out::println) + .filter(event -> event.getId().equals("DocUrl")).count(), 1); + } +} diff --git a/gradle-plugin-examples/tutorial/README.md b/gradle-plugin-examples/tutorial/README.md new file mode 100644 index 0000000..5f0877e --- /dev/null +++ b/gradle-plugin-examples/tutorial/README.md @@ -0,0 +1,26 @@ +### Smithy Gradle Plugin Tutorial + +An example Smithy project that demonstrates how to use the [Smithy Gradle plugins](https://github.com/smithy-lang/smithy-gradle-plugin) +by creating a simple Cafe service model. + +This project: +1. Sets up a `lib` project containing Smithy models that are shared across other Smithy projects. +2. Uses these shared models as a dependency of the `service` package to create a Cafe service model. +3. Create a custom linter in the `lib` package that can be used by consumers of the `lib` package to + check for `externalDocumentation` links that do not match an expected site address. + +For more information on the use of the Smithy Gradle plugins see the [plugin guide](https://smithy.io/2.0/guides/gradle-plugin/index.html). + +## Building +To build this package run the following from the root of the package: + +``` +./gradlew clean build +``` + +This will generate a `build` directory in both the `lib` and `service` subprojects +containing the build artifacts generated by gradle. + +## Distribution +If you want to distribute either of the JAR files created by this package, consider adding the +[maven-publish](https://docs.gradle.org/current/userguide/publishing_maven.html) plugin to the project to publish the JAR to a maven repository. diff --git a/gradle-plugin-examples/tutorial/gradle/libs.versions.toml b/gradle-plugin-examples/tutorial/gradle/libs.versions.toml new file mode 100644 index 0000000..15c9ba0 --- /dev/null +++ b/gradle-plugin-examples/tutorial/gradle/libs.versions.toml @@ -0,0 +1,9 @@ +[versions] +commons-math3 = "3.6.1" +guava = "32.1.2-jre" +junit-jupiter = "5.10.0" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } diff --git a/gradle-plugin-examples/tutorial/lib/build.gradle.kts b/gradle-plugin-examples/tutorial/lib/build.gradle.kts new file mode 100644 index 0000000..de6616e --- /dev/null +++ b/gradle-plugin-examples/tutorial/lib/build.gradle.kts @@ -0,0 +1,45 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java library project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.5/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the java-library plugin for API and implementation separation. + `java-library` + // Build Smithy models and add them to the generated library JAR. + id("software.amazon.smithy.gradle.smithy-jar").version("0.10.1") +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit Jupiter for testing. + testImplementation(libs.junit.jupiter) + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api(libs.commons.math3) + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation(libs.guava) + + implementation("software.amazon.smithy:smithy-model:1.45.0") +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/gradle-plugin-examples/tutorial/lib/model/common.smithy b/gradle-plugin-examples/tutorial/lib/model/common.smithy new file mode 100644 index 0000000..c1b8afb --- /dev/null +++ b/gradle-plugin-examples/tutorial/lib/model/common.smithy @@ -0,0 +1,23 @@ +$version: "2.0" + +namespace com.example.cafe.common + +/// The price of a menu item should be a positive number +@range(min: 0.0) +bigDecimal Price + +enum Style { + ICED + HOT +} + +enum Type { + COFFEE + TEA + COCOA +} + +/// ID used to Identify a menu item +@length(min: 1, max: 64) +@pattern("^[a-zA-Z0-9_]$") +string MenuItemId diff --git a/gradle-plugin-examples/tutorial/lib/smithy-build.json b/gradle-plugin-examples/tutorial/lib/smithy-build.json new file mode 100644 index 0000000..703ffb7 --- /dev/null +++ b/gradle-plugin-examples/tutorial/lib/smithy-build.json @@ -0,0 +1,3 @@ +{ + "version": "1.0" +} diff --git a/gradle-plugin-examples/tutorial/lib/src/main/java/com/example/cafe/DocUrlValidator.java b/gradle-plugin-examples/tutorial/lib/src/main/java/com/example/cafe/DocUrlValidator.java new file mode 100644 index 0000000..2f352fc --- /dev/null +++ b/gradle-plugin-examples/tutorial/lib/src/main/java/com/example/cafe/DocUrlValidator.java @@ -0,0 +1,35 @@ +package com.example.cafe; + +import java.util.List; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.traits.ExternalDocumentationTrait; +import software.amazon.smithy.model.validation.AbstractValidator; +import software.amazon.smithy.model.validation.ValidationEvent; +import software.amazon.smithy.model.validation.ValidatorService; + +public final class DocUrlValidator extends AbstractValidator { + private static final String CAFE_SITE_NAME = "cafe.example.smithy.io"; + + public static final class Provider extends ValidatorService.Provider { + public Provider() { + super(DocUrlValidator.class, DocUrlValidator::new); + } + } + + @Override + public List validate(Model model) { + // Find every shape that violates the linter and return a list + // of ValidationEvents. + return model.shapes() + .filter(shape -> shape.hasTrait(ExternalDocumentationTrait.class)) + .flatMap(shape -> shape.expectTrait(ExternalDocumentationTrait.class) + // Get a stream of Name -> URL entries + .getUrls().entrySet().stream() + // Get all URLs that do not match expected internal site name + .filter(entry -> !entry.getValue().contains(CAFE_SITE_NAME)) + // Emit a helpful warning message for each invalid URL + .map(entry -> warning(shape, String.format("External url `%s` for `%s` is invalid." + + " Expected URL to start with " + CAFE_SITE_NAME, entry.getValue(), entry.getKey()))) + ).toList(); + } +} diff --git a/gradle-plugin-examples/tutorial/lib/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.ValidatorService b/gradle-plugin-examples/tutorial/lib/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.ValidatorService new file mode 100644 index 0000000..1ef2e34 --- /dev/null +++ b/gradle-plugin-examples/tutorial/lib/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.ValidatorService @@ -0,0 +1,2 @@ +com.example.cafe.DocUrlValidator$Provider + diff --git a/gradle-plugin-examples/tutorial/service/build.gradle.kts b/gradle-plugin-examples/tutorial/service/build.gradle.kts new file mode 100644 index 0000000..83b2f8d --- /dev/null +++ b/gradle-plugin-examples/tutorial/service/build.gradle.kts @@ -0,0 +1,15 @@ + +plugins { + `java-library` + id("software.amazon.smithy.gradle.smithy-jar").version("0.10.1") +} + +repositories { + mavenCentral() +} + +dependencies { + // Use common models + implementation(project(":lib")) +} + diff --git a/gradle-plugin-examples/tutorial/service/model/service.smithy b/gradle-plugin-examples/tutorial/service/model/service.smithy new file mode 100644 index 0000000..979787b --- /dev/null +++ b/gradle-plugin-examples/tutorial/service/model/service.smithy @@ -0,0 +1,58 @@ +$version: "2.0" + +metadata validators = [ + { + name: "DocUrl" + } +] + +namespace com.example.cafe.service + +// Add common shapes from `lib` subproject +use com.example.cafe.common#MenuItemId + +use com.example.cafe.common#Price +use com.example.cafe.common#Style +use com.example.cafe.common#Type + +/// Provides an API for ordering coffee +service CoffeeService { + version: "2024-03-30" + resources: [ + Order + ] +} + +@externalDocumentation(wikipedia: "https://en.wikipedia.org/wiki/Order") +resource Order { + identifiers: { id: MenuItemId } + properties: { price: Price, style: Style, type: Type } + create: CreateOrder + read: GetOrder +} + +operation CreateOrder { + input := for Order { + $style + $type + } + + output := for Order { + @required + $id + } +} + +@readonly +operation GetOrder { + input := for Order { + @required + $id + } + + output := for Order { + $price + $style + $type + } +} diff --git a/gradle-plugin-examples/tutorial/service/smithy-build.json b/gradle-plugin-examples/tutorial/service/smithy-build.json new file mode 100644 index 0000000..703ffb7 --- /dev/null +++ b/gradle-plugin-examples/tutorial/service/smithy-build.json @@ -0,0 +1,3 @@ +{ + "version": "1.0" +} diff --git a/gradle-plugin-examples/tutorial/settings.gradle.kts b/gradle-plugin-examples/tutorial/settings.gradle.kts new file mode 100644 index 0000000..acc9b39 --- /dev/null +++ b/gradle-plugin-examples/tutorial/settings.gradle.kts @@ -0,0 +1,15 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.5/userguide/building_swift_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the foojay-resolver plugin to allow automatic download of JDKs + id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" +} + +rootProject.name = "smithy-gradle-tutorial" +include("lib") +include("service") diff --git a/settings.gradle.kts b/settings.gradle.kts index 164294e..1b50cfb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,3 +45,10 @@ include(":linting-and-validation-examples:integ:common-linting-configuration-tes include(":linting-and-validation-examples:integ:custom-linter-test") include(":linting-and-validation-examples:integ:custom-validator-test") include(":linting-and-validation-examples:integ:decorators-test") + +// ---- Gradle Plugin Examples ----- +// Templates +includeBuild("gradle-plugin-examples/tutorial") + +// Integ test +include(":gradle-plugin-examples:integ:tutorial") diff --git a/smithy-templates.json b/smithy-templates.json index cb2f0c3..9c727d0 100644 --- a/smithy-templates.json +++ b/smithy-templates.json @@ -121,6 +121,19 @@ ".gitignore", ".gitattributes" ] + }, + "smithy-gradle-tutorial": { + "documentation": "Tutorial project that demonstrates how to use the Smithy Gradle plugins.", + "path": "gradle-plugin-examples/tutorial", + "include": [ + "config/", + "gradle/", + "gradlew", + "gradlew.bat", + "gradle.properties", + ".gitignore", + ".gitattributes" + ] } } } From a9277dad3283ae5f8665d4493bdc6e6f4df00cbf Mon Sep 17 00:00:00 2001 From: Hunter Mellema <124718352+hpmellema@users.noreply.github.com> Date: Wed, 20 Mar 2024 09:15:50 -0600 Subject: [PATCH 2/3] Update gradle-plugin-examples/tutorial/README.md Co-authored-by: Jordon Phillips --- gradle-plugin-examples/tutorial/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugin-examples/tutorial/README.md b/gradle-plugin-examples/tutorial/README.md index 5f0877e..d1c84f9 100644 --- a/gradle-plugin-examples/tutorial/README.md +++ b/gradle-plugin-examples/tutorial/README.md @@ -1,6 +1,6 @@ ### Smithy Gradle Plugin Tutorial -An example Smithy project that demonstrates how to use the [Smithy Gradle plugins](https://github.com/smithy-lang/smithy-gradle-plugin) +An example Smithy project that demonstrates how to use [Smithy Gradle plugins](https://github.com/smithy-lang/smithy-gradle-plugin) by creating a simple Cafe service model. This project: From a5c2d0e3f18319db050d8df3319a6bb01625cae6 Mon Sep 17 00:00:00 2001 From: Hunter Mellema Date: Wed, 20 Mar 2024 11:39:25 -0600 Subject: [PATCH 3/3] Update dependency versions --- gradle-plugin-examples/integ/tutorial/build.gradle.kts | 6 +++--- gradle-plugin-examples/tutorial/gradle/libs.versions.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle-plugin-examples/integ/tutorial/build.gradle.kts b/gradle-plugin-examples/integ/tutorial/build.gradle.kts index 9971c55..5248153 100644 --- a/gradle-plugin-examples/integ/tutorial/build.gradle.kts +++ b/gradle-plugin-examples/integ/tutorial/build.gradle.kts @@ -18,8 +18,8 @@ dependencies { val smithyVersion: String by project testImplementation("software.amazon.smithy:smithy-model:$smithyVersion") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0") - testImplementation("org.junit.jupiter:junit-jupiter-engine:5.4.0") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.4.0") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") testImplementation("org.hamcrest:hamcrest:2.1") } diff --git a/gradle-plugin-examples/tutorial/gradle/libs.versions.toml b/gradle-plugin-examples/tutorial/gradle/libs.versions.toml index 15c9ba0..efe0323 100644 --- a/gradle-plugin-examples/tutorial/gradle/libs.versions.toml +++ b/gradle-plugin-examples/tutorial/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] commons-math3 = "3.6.1" guava = "32.1.2-jre" -junit-jupiter = "5.10.0" +junit-jupiter = "5.10.2" [libraries] commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" }