From 8e40e20d1b67e5e5f17f07ddcd395b11fbe64b36 Mon Sep 17 00:00:00 2001 From: Hunter Mellema Date: Tue, 11 Feb 2025 15:51:48 -0700 Subject: [PATCH 1/2] Add quickstart example for smithy-java --- .../model/main.smithy | 4 +- .../tutorial/service/model/service.smithy | 10 ++- gradle.properties | 1 + settings.gradle.kts | 7 ++ smithy-java-examples/README.md | 4 + .../quickstart-java/README.md | 30 +++++++ .../quickstart-java/build.gradle.kts | 8 ++ .../quickstart-java/client/build.gradle.kts | 65 +++++++++++++++ .../quickstart-java/client/smithy-build.json | 12 +++ .../java/client/example/TestRunner.java | 58 +++++++++++++ .../quickstart-java/gradle.properties | 3 + .../quickstart-java/lib/build.gradle.kts | 23 +++++ .../quickstart-java/lib/model/common.smithy | 27 ++++++ .../quickstart-java/lib/model/main.smithy | 28 +++++++ .../quickstart-java/lib/model/order.smithy | 83 +++++++++++++++++++ .../quickstart-java/lib/smithy-build.json | 3 + .../quickstart-java/license.txt | 4 + .../quickstart-java/plugins/build.gradle.kts | 11 +++ .../java/example/plugins/ExamplePlugin.java | 24 ++++++ .../quickstart-java/server/build.gradle.kts | 39 +++++++++ .../quickstart-java/server/smithy-build.json | 10 +++ .../java/server/example/CafeService.java | 46 ++++++++++ .../java/server/example/CreateOrder.java | 36 ++++++++ .../smithy/java/server/example/GetMenu.java | 59 +++++++++++++ .../smithy/java/server/example/GetOrder.java | 39 +++++++++ .../io/smithy/java/server/example/Order.java | 19 +++++ .../java/server/example/OrderTracker.java | 40 +++++++++ .../quickstart-java/settings.gradle.kts | 21 +++++ smithy-templates.json | 12 +++ 29 files changed, 723 insertions(+), 3 deletions(-) create mode 100644 smithy-java-examples/README.md create mode 100644 smithy-java-examples/quickstart-java/README.md create mode 100644 smithy-java-examples/quickstart-java/build.gradle.kts create mode 100644 smithy-java-examples/quickstart-java/client/build.gradle.kts create mode 100644 smithy-java-examples/quickstart-java/client/smithy-build.json create mode 100644 smithy-java-examples/quickstart-java/client/src/it/java/io/smithy/java/client/example/TestRunner.java create mode 100644 smithy-java-examples/quickstart-java/gradle.properties create mode 100644 smithy-java-examples/quickstart-java/lib/build.gradle.kts create mode 100644 smithy-java-examples/quickstart-java/lib/model/common.smithy create mode 100644 smithy-java-examples/quickstart-java/lib/model/main.smithy create mode 100644 smithy-java-examples/quickstart-java/lib/model/order.smithy create mode 100644 smithy-java-examples/quickstart-java/lib/smithy-build.json create mode 100644 smithy-java-examples/quickstart-java/license.txt create mode 100644 smithy-java-examples/quickstart-java/plugins/build.gradle.kts create mode 100644 smithy-java-examples/quickstart-java/plugins/src/main/java/io/smithy/java/example/plugins/ExamplePlugin.java create mode 100644 smithy-java-examples/quickstart-java/server/build.gradle.kts create mode 100644 smithy-java-examples/quickstart-java/server/smithy-build.json create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CafeService.java create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CreateOrder.java create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetMenu.java create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetOrder.java create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/Order.java create mode 100644 smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/OrderTracker.java create mode 100644 smithy-java-examples/quickstart-java/settings.gradle.kts diff --git a/custom-trait-examples/integ/custom-trait-with-java-validator-test/model/main.smithy b/custom-trait-examples/integ/custom-trait-with-java-validator-test/model/main.smithy index 1ef1b5b..69a3c02 100644 --- a/custom-trait-examples/integ/custom-trait-with-java-validator-test/model/main.smithy +++ b/custom-trait-examples/integ/custom-trait-with-java-validator-test/model/main.smithy @@ -10,7 +10,9 @@ use example.traits#resourceMetadata associatedStructures: [ForecastStruct] ) resource Forecast { - identifiers: { forecastId: ForecastId } + identifiers: { + forecastId: ForecastId + } } string ForecastId diff --git a/gradle-plugin-examples/tutorial/service/model/service.smithy b/gradle-plugin-examples/tutorial/service/model/service.smithy index 25db1f4..bb6dc30 100644 --- a/gradle-plugin-examples/tutorial/service/model/service.smithy +++ b/gradle-plugin-examples/tutorial/service/model/service.smithy @@ -26,8 +26,14 @@ service DrinkService { @externalDocumentation(wikipedia: "https://en.wikipedia.org/wiki/Order") resource Order { - identifiers: { id: MenuItemId } - properties: { price: Price, style: Style, type: Type } + identifiers: { + id: MenuItemId + } + properties: { + price: Price + style: Style + type: Type + } create: CreateOrder read: GetOrder } diff --git a/gradle.properties b/gradle.properties index 352fd85..1a53326 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ smithyVersion=1.51.0 smithyGradleVersion=1.1.0 +smithyJavaVersion=0.0.1 diff --git a/settings.gradle.kts b/settings.gradle.kts index 2f1ef0e..348dc6e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -53,3 +53,10 @@ includeBuild("gradle-plugin-examples/tutorial") // Integ test include(":gradle-plugin-examples:integ:tutorial") + +// ---- Smithy-Java examples ---- +// templates +includeBuild("smithy-java-examples/quickstart-java") + +// integration tests +include(":smithy-java-examples:integ") diff --git a/smithy-java-examples/README.md b/smithy-java-examples/README.md new file mode 100644 index 0000000..a8368cd --- /dev/null +++ b/smithy-java-examples/README.md @@ -0,0 +1,4 @@ +# Linting and Validation +The examples in this directory demonstrate the use of the [Smithy Java](https://github.com/smithy-lang/smithy-java) code generator. + +Additional examples cna be found [in the Smithy Java repository](https://github.com/smithy-lang/smithy-java/tree/main/examples). diff --git a/smithy-java-examples/quickstart-java/README.md b/smithy-java-examples/quickstart-java/README.md new file mode 100644 index 0000000..539950e --- /dev/null +++ b/smithy-java-examples/quickstart-java/README.md @@ -0,0 +1,30 @@ +## Smithy-Java Quickstart + +This project provides a template to get started using [Smithy Java](https://github.com/smithy-lang/smithy-java/) +to create Java clients and servers. + +For more information on this example see the [Smithy Java Quickstart Guide](https://smithy.io/2.0/java/quickstart.html). + +### Layout +- `lib/`: Common package for the service API model. Shared by both client and server. +- `server/`: Code generated Server that implements stubbed operations code-generated from the service model. +- `client/`: Code generated client that can call the server. +- `plugins/`: a package defining client plugins. + +### Usage + +Then, to create a new project from this template, use the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) +`init` command as follows: + +```console +smithy init -t smithy-java-quickstart +``` + +### Running and testing server + +To run and test the server, run `./gradlew run` from the root of a project created from this +template. That will start the server running on port `8888`. + +Once the server is running you can call the server using curl or by executing the integration tests in `client` subproject: +- `curl`: `curl -H "content-type: application/json" -d '{"coffeeType": "LATTE"}' -X POST localhost:8888/order` +- integration tests: `./gradlew :client:integ` diff --git a/smithy-java-examples/quickstart-java/build.gradle.kts b/smithy-java-examples/quickstart-java/build.gradle.kts new file mode 100644 index 0000000..fca222d --- /dev/null +++ b/smithy-java-examples/quickstart-java/build.gradle.kts @@ -0,0 +1,8 @@ + +// Add repositories for all subprojects to resolve dependencies. +allprojects { + repositories { + mavenLocal() + mavenCentral() + } +} diff --git a/smithy-java-examples/quickstart-java/client/build.gradle.kts b/smithy-java-examples/quickstart-java/client/build.gradle.kts new file mode 100644 index 0000000..2f8d063 --- /dev/null +++ b/smithy-java-examples/quickstart-java/client/build.gradle.kts @@ -0,0 +1,65 @@ +description = "Cafe service client" + +plugins { + `java-library` + // Executes smithy-build process to generate client code + id("software.amazon.smithy.gradle.smithy-base") +} + +dependencies { + val smithyJavaVersion: String by project + + // === Code generators === + smithyBuild("software.amazon.smithy.java.codegen:plugins:$smithyJavaVersion") + + // === Service model === + implementation(project(":lib")) + + // === Client Plugins === + implementation(project(":plugins")) + + // === Client Dependencies === + // Core client dependency required by generated client code. + implementation("software.amazon.smithy.java:client-core:$smithyJavaVersion") + // Add client implementation of `RestJson1` protocol + implementation("software.amazon.smithy.java:aws-client-restjson:$smithyJavaVersion") + + // Test dependencies + testImplementation("org.junit.jupiter:junit-jupiter:5.7.1") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +afterEvaluate { + val clientPath = smithy.getPluginProjectionPath(smithy.sourceProjection.get(), "java-client-codegen") + sourceSets { + // Add generated client source code to the main sourceSet + main { + java { + srcDir(clientPath) + } + } + // Set up integration testing tasks source set + create("it") { + compileClasspath += main.get().output + configurations["testRuntimeClasspath"] + configurations["testCompileClasspath"] + runtimeClasspath += output + compileClasspath + test.get().runtimeClasspath + test.get().output + } + } +} + +tasks { + // Ensure compilation happens after source-code generation + val smithyBuild by getting + compileJava { + dependsOn(smithyBuild) + } + + // This is set up integ tests separate from the `test` task to + // avoid automatically running tests as part of build. + val integ by registering(Test::class) { + useJUnitPlatform() + testClassesDirs = sourceSets["it"].output.classesDirs + classpath = sourceSets["it"].runtimeClasspath + // Allow the integ tests to print to stdout/stderr + testLogging.showStandardStreams = true + } +} diff --git a/smithy-java-examples/quickstart-java/client/smithy-build.json b/smithy-java-examples/quickstart-java/client/smithy-build.json new file mode 100644 index 0000000..32409c1 --- /dev/null +++ b/smithy-java-examples/quickstart-java/client/smithy-build.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "plugins": { + "java-client-codegen": { + "service": "com.example#CoffeeShop", + "namespace": "io.smithy.java.client.example", + "headerFile": "../license.txt", + "protocol": "aws.protocols#restJson1", + "defaultPlugins": ["io.smithy.java.example.plugins.ExamplePlugin"] + } + } +} diff --git a/smithy-java-examples/quickstart-java/client/src/it/java/io/smithy/java/client/example/TestRunner.java b/smithy-java-examples/quickstart-java/client/src/it/java/io/smithy/java/client/example/TestRunner.java new file mode 100644 index 0000000..171aecb --- /dev/null +++ b/smithy-java-examples/quickstart-java/client/src/it/java/io/smithy/java/client/example/TestRunner.java @@ -0,0 +1,58 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.client.example; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import io.smithy.java.client.example.client.CoffeeShopClient; +import io.smithy.java.client.example.model.CoffeeType; +import io.smithy.java.client.example.model.CreateOrderInput; +import io.smithy.java.client.example.model.GetMenuInput; +import io.smithy.java.client.example.model.GetOrderInput; +import io.smithy.java.client.example.model.OrderNotFound; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; + + +public class TestRunner { + private final CoffeeShopClient client = CoffeeShopClient.builder().build(); + + @Test + public void getMenu() { + var menu = client.getMenu(GetMenuInput.builder().build()); + System.out.println(menu); + } + + @Test + public void createOrder() throws InterruptedException { + // Create an order + var createRequest = CreateOrderInput.builder().coffeeType(CoffeeType.COLD_BREW).build(); + var createResponse = client.createOrder(createRequest); + System.out.println("Created request with id = " + createResponse.id()); + + // Get the order. Should still be in progress. + var getRequest = GetOrderInput.builder().id(createResponse.id()).build(); + var getResponse1 = client.getOrder(getRequest); + System.out.println("Got order with id = " + getResponse1.id()); + + // Give order some time to complete + System.out.println("Waiting for order to complete...."); + TimeUnit.SECONDS.sleep(5); + + // Get the order again. + var getResponse2 = client.getOrder(getRequest); + System.out.println("Completed Order :" + getResponse2); + } + + @Test + void errorsOutIfOrderDoesNotExist() throws InterruptedException { + var getRequest = GetOrderInput.builder().id(UUID.randomUUID().toString()).build(); + var orderNotFound = assertThrows(OrderNotFound.class, () -> client.getOrder(getRequest)); + assertEquals(orderNotFound.orderId(), getRequest.id()); + } +} diff --git a/smithy-java-examples/quickstart-java/gradle.properties b/smithy-java-examples/quickstart-java/gradle.properties new file mode 100644 index 0000000..1a53326 --- /dev/null +++ b/smithy-java-examples/quickstart-java/gradle.properties @@ -0,0 +1,3 @@ +smithyVersion=1.51.0 +smithyGradleVersion=1.1.0 +smithyJavaVersion=0.0.1 diff --git a/smithy-java-examples/quickstart-java/lib/build.gradle.kts b/smithy-java-examples/quickstart-java/lib/build.gradle.kts new file mode 100644 index 0000000..3cbc116 --- /dev/null +++ b/smithy-java-examples/quickstart-java/lib/build.gradle.kts @@ -0,0 +1,23 @@ +description = "Smithy definition of a Cafe service." + +plugins { + `java-library` + // Packages the models in this package into a jar for sharing/distribution by other packages + id("software.amazon.smithy.gradle.smithy-jar") +} + +dependencies { + val smithyVersion: String by project + + // Adds the `@restJson1` protocol trait + api("software.amazon.smithy:smithy-aws-traits:$smithyVersion") +} + +// Helps the Smithy IntelliJ plugin identify models +sourceSets { + main { + java { + srcDir("model") + } + } +} diff --git a/smithy-java-examples/quickstart-java/lib/model/common.smithy b/smithy-java-examples/quickstart-java/lib/model/common.smithy new file mode 100644 index 0000000..192c4f5 --- /dev/null +++ b/smithy-java-examples/quickstart-java/lib/model/common.smithy @@ -0,0 +1,27 @@ +$version: "2" + +namespace com.example + +/// An enum describing the types of coffees available +enum CoffeeType { + DRIP + POUR_OVER + LATTE + ESPRESSO + COLD_BREW +} + +/// A structure which defines a coffee item which can be ordered +structure CoffeeItem { + /// A type of coffee + @required + type: CoffeeType + + @required + description: String +} + +/// A list of coffee items +list CoffeeItems { + member: CoffeeItem +} diff --git a/smithy-java-examples/quickstart-java/lib/model/main.smithy b/smithy-java-examples/quickstart-java/lib/model/main.smithy new file mode 100644 index 0000000..92bc2d6 --- /dev/null +++ b/smithy-java-examples/quickstart-java/lib/model/main.smithy @@ -0,0 +1,28 @@ +$version: "2" + +namespace com.example + +use aws.protocols#restJson1 + +/// Allows users to retrieve a menu, create a coffee order, and +/// and to view the status of their orders +@title("Coffee Shop Service") +@restJson1 +service CoffeeShop { + version: "2024-08-23" + operations: [ + GetMenu + ] + resources: [ + Order + ] +} + +/// Retrieve the menu +@http(method: "GET", uri: "/menu") +@readonly +operation GetMenu { + output := { + items: CoffeeItems + } +} diff --git a/smithy-java-examples/quickstart-java/lib/model/order.smithy b/smithy-java-examples/quickstart-java/lib/model/order.smithy new file mode 100644 index 0000000..45d6264 --- /dev/null +++ b/smithy-java-examples/quickstart-java/lib/model/order.smithy @@ -0,0 +1,83 @@ +$version: "2.0" + +namespace com.example + +/// An Order resource, which has an id and describes an order by the type of coffee +/// and the order's status +resource Order { + identifiers: { + id: Uuid + } + properties: { + coffeeType: CoffeeType + status: OrderStatus + } + read: GetOrder + create: CreateOrder +} + +/// Create an order +@idempotent +@http(method: "POST", uri: "/order") +operation CreateOrder { + input := for Order { + @required + $coffeeType + } + + output := for Order { + @required + $id + + @required + $coffeeType + + @required + $status + } +} + +/// Retrieve an order +@readonly +@http(method: "GET", uri: "/order/{id}") +operation GetOrder { + input := for Order { + @httpLabel + @required + $id + } + + output := for Order { + @required + $id + + @required + $coffeeType + + @required + $status + } + + errors: [ + OrderNotFound + ] +} + +/// An error indicating an order could not be found +@httpError(404) +@error("client") +structure OrderNotFound { + message: String + orderId: Uuid +} + +/// An identifier to describe a unique order +@length(min: 1, max: 128) +@pattern("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") +string Uuid + +/// An enum describing the status of an order +enum OrderStatus { + IN_PROGRESS + COMPLETED +} diff --git a/smithy-java-examples/quickstart-java/lib/smithy-build.json b/smithy-java-examples/quickstart-java/lib/smithy-build.json new file mode 100644 index 0000000..703ffb7 --- /dev/null +++ b/smithy-java-examples/quickstart-java/lib/smithy-build.json @@ -0,0 +1,3 @@ +{ + "version": "1.0" +} diff --git a/smithy-java-examples/quickstart-java/license.txt b/smithy-java-examples/quickstart-java/license.txt new file mode 100644 index 0000000..edaafd8 --- /dev/null +++ b/smithy-java-examples/quickstart-java/license.txt @@ -0,0 +1,4 @@ +/* + * Example file license header. + * File header line two + */ diff --git a/smithy-java-examples/quickstart-java/plugins/build.gradle.kts b/smithy-java-examples/quickstart-java/plugins/build.gradle.kts new file mode 100644 index 0000000..8106986 --- /dev/null +++ b/smithy-java-examples/quickstart-java/plugins/build.gradle.kts @@ -0,0 +1,11 @@ +description = "Defines plugins to configure generated clients" + +plugins { + `java-library` +} + +dependencies { + val smithyJavaVersion: String by project + + implementation("software.amazon.smithy.java:client-core:$smithyJavaVersion") +} diff --git a/smithy-java-examples/quickstart-java/plugins/src/main/java/io/smithy/java/example/plugins/ExamplePlugin.java b/smithy-java-examples/quickstart-java/plugins/src/main/java/io/smithy/java/example/plugins/ExamplePlugin.java new file mode 100644 index 0000000..ab1b5f9 --- /dev/null +++ b/smithy-java-examples/quickstart-java/plugins/src/main/java/io/smithy/java/example/plugins/ExamplePlugin.java @@ -0,0 +1,24 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.example.plugins; + +import software.amazon.smithy.java.client.core.ClientConfig; +import software.amazon.smithy.java.client.core.ClientPlugin; +import software.amazon.smithy.java.client.core.endpoint.EndpointResolver; + +/** + * Example plugin that sets the static endpoint for the generated client. + * + *

Plugins allow clients to be configured in a repeatable way. + */ +public class ExamplePlugin implements ClientPlugin { + private static final EndpointResolver STATIC_ENDPOINT = EndpointResolver.staticEndpoint("http://localhost:8888"); + + @Override + public void configureClient(ClientConfig.Builder builder) { + builder.endpointResolver(STATIC_ENDPOINT); + } +} diff --git a/smithy-java-examples/quickstart-java/server/build.gradle.kts b/smithy-java-examples/quickstart-java/server/build.gradle.kts new file mode 100644 index 0000000..2cffec4 --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/build.gradle.kts @@ -0,0 +1,39 @@ +description = "Cafe service server implementation" + +plugins { + `java-library` + application + // Executes smithy-build process to generate server stubs + id("software.amazon.smithy.gradle.smithy-base") +} + +dependencies { + val smithyJavaVersion: String by project + + // === Code generators === + smithyBuild("software.amazon.smithy.java.codegen:plugins:$smithyJavaVersion") + + // === Service model === + implementation(project(":lib")) + + // === Server dependencies === + // Adds an HTTP server implementation based on netty + implementation("software.amazon.smithy.java:server-netty:$smithyJavaVersion") + // Adds the server implementation of the `RestJson1` protocol + implementation("software.amazon.smithy.java:aws-server-restjson:$smithyJavaVersion") +} + +// Add generated source code to the compilation sourceSet +afterEvaluate { + val serverPath = smithy.getPluginProjectionPath(smithy.sourceProjection.get(), "java-server-codegen") + sourceSets.main.get().java.srcDir(serverPath) +} + +tasks.named("compileJava") { + dependsOn("smithyBuild") +} + +// Use the application plugin to start the service via the `run` task. +application { + mainClass = "io.smithy.java.server.example.CafeService" +} diff --git a/smithy-java-examples/quickstart-java/server/smithy-build.json b/smithy-java-examples/quickstart-java/server/smithy-build.json new file mode 100644 index 0000000..e0ce858 --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/smithy-build.json @@ -0,0 +1,10 @@ +{ + "version": "1.0", + "plugins": { + "java-server-codegen": { + "service": "com.example#CoffeeShop", + "namespace": "io.smithy.java.server.example", + "headerFile": "../license.txt" + } + } +} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CafeService.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CafeService.java new file mode 100644 index 0000000..d75eecb --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CafeService.java @@ -0,0 +1,46 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.service.CoffeeShop; +import java.net.URI; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; +import software.amazon.smithy.java.server.Server; + +public class CafeService implements Runnable { + private static final Logger LOGGER = Logger.getLogger(CafeService.class.getName()); + + public static void main(String... args) throws InterruptedException, ExecutionException { + new CafeService().run(); + } + + @Override + public void run() { + Server server = Server.builder() + .endpoints(URI.create("http://localhost:8888")) + .addService( + CoffeeShop.builder() + .addCreateOrderOperation(new CreateOrder()) + .addGetMenuOperation(new GetMenu()) + .addGetOrderOperation(new GetOrder()) + .build() + ) + .build(); + LOGGER.info("Starting server..."); + server.start(); + try { + Thread.currentThread().join(); + } catch (InterruptedException e) { + LOGGER.info("Stopping server..."); + try { + server.shutdown().get(); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); + } + } + } +} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CreateOrder.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CreateOrder.java new file mode 100644 index 0000000..e561a4e --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/CreateOrder.java @@ -0,0 +1,36 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.model.CreateOrderInput; +import io.smithy.java.server.example.model.CreateOrderOutput; +import io.smithy.java.server.example.model.OrderStatus; +import io.smithy.java.server.example.service.CreateOrderOperation; +import java.util.UUID; + +import java.util.logging.Logger; +import software.amazon.smithy.java.server.RequestContext; + +/** + * Create an order for a coffee. + */ +final class CreateOrder implements CreateOrderOperation { + private static final Logger LOGGER = Logger.getLogger(CreateOrder.class.getName()); + + @Override + public CreateOrderOutput createOrder(CreateOrderInput input, RequestContext context) { + var id = UUID.randomUUID(); + OrderTracker.putOrder(new Order(id, input.coffeeType(), OrderStatus.IN_PROGRESS)); + + LOGGER.info("Created order " + id + " for a " + input.coffeeType()); + + return CreateOrderOutput.builder() + .id(id.toString()) + .coffeeType(input.coffeeType()) + .status(OrderStatus.IN_PROGRESS) + .build(); + } +} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetMenu.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetMenu.java new file mode 100644 index 0000000..b7278af --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetMenu.java @@ -0,0 +1,59 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.model.CoffeeItem; +import io.smithy.java.server.example.model.CoffeeType; +import io.smithy.java.server.example.model.GetMenuInput; +import io.smithy.java.server.example.model.GetMenuOutput; +import io.smithy.java.server.example.service.GetMenuOperation; +import java.util.List; + +import software.amazon.smithy.java.server.RequestContext; + +/** + * Returns the menu for the coffee shop + */ +final class GetMenu implements GetMenuOperation { + private static final List MENU = List.of( + CoffeeItem.builder() + .typeMember(CoffeeType.DRIP) + .description(""" + A clean-bodied, rounder, and more simplistic flavour profile. + Often praised for mellow and less intense notes. + Far less concentrated than espresso. + """) + .build(), + CoffeeItem.builder() + .typeMember(CoffeeType.POUR_OVER) + .description(""" + Similar to drip coffee, but with a process that brings out more subtle nuances in flavor. + More concentrated than drip, but less than espresso. + """) + .build(), + CoffeeItem.builder() + .typeMember(CoffeeType.LATTE) + .description(""" + A creamier, milk-based drink made with espresso. + A subtle coffee taste, with smooth texture. + High milk-to-coffee ratio. + """) + .build(), + CoffeeItem.builder() + .typeMember(CoffeeType.ESPRESSO) + .description(""" + A highly concentrated form of coffee, brewed under high pressure. + Syrupy, thick liquid in a small serving size. + Full bodied and intensely aromatic. + """) + .build() + ); + + @Override + public GetMenuOutput getMenu(GetMenuInput input, RequestContext context) { + return GetMenuOutput.builder().items(MENU).build(); + } +} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetOrder.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetOrder.java new file mode 100644 index 0000000..d7e60d9 --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/GetOrder.java @@ -0,0 +1,39 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.model.GetOrderInput; +import io.smithy.java.server.example.model.GetOrderOutput; +import io.smithy.java.server.example.model.OrderNotFound; +import io.smithy.java.server.example.service.GetOrderOperation; +import java.util.UUID; +import java.util.logging.Logger; +import software.amazon.smithy.java.server.RequestContext; + +/** + * Returns the specified order if found. + */ +final class GetOrder implements GetOrderOperation { + private static final Logger LOGGER = Logger.getLogger(GetOrder.class.getName()); + + @Override + public GetOrderOutput getOrder(GetOrderInput input, RequestContext context) { + var order = OrderTracker.getOrderById(UUID.fromString(input.id())); + if (order == null) { + LOGGER.warning("Order not found: " + input.id()); + throw OrderNotFound.builder() + .orderId(input.id()) + .message("Order not found") + .build(); + } + LOGGER.info("Order " + input.id() + " found."); + return GetOrderOutput.builder() + .id(input.id()) + .coffeeType(order.type()) + .status(order.status()) + .build(); + } +} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/Order.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/Order.java new file mode 100644 index 0000000..a2efa2d --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/Order.java @@ -0,0 +1,19 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.model.CoffeeType; +import io.smithy.java.server.example.model.OrderStatus; +import java.util.UUID; + +/** + * A coffee drink order. + * + * @param id UUID of the order + * @param type Type of drink for the order + * @param status status of the order. + */ +public record Order(UUID id, CoffeeType type, OrderStatus status) {} diff --git a/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/OrderTracker.java b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/OrderTracker.java new file mode 100644 index 0000000..9474957 --- /dev/null +++ b/smithy-java-examples/quickstart-java/server/src/main/java/io/smithy/java/server/example/OrderTracker.java @@ -0,0 +1,40 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: MIT-0 + */ + +package io.smithy.java.server.example; + +import io.smithy.java.server.example.model.OrderStatus; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +/** + * This class is a stand-in for a database. + */ +final class OrderTracker { + private static final Logger LOGGER = Logger.getLogger(OrderTracker.class.getName()); + private static final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); + private static final Map ORDERS = new ConcurrentHashMap<>(); + + public static void putOrder(Order order) { + ORDERS.put(order.id(), order); + + // Start a process to complete the order in the background. + executor.schedule(() -> completeOrder(order), 5, TimeUnit.SECONDS); + } + + private static void completeOrder(Order order) { + ORDERS.put(order.id(), new Order(order.id(), order.type(), OrderStatus.COMPLETED)); + LOGGER.info("Order completed: " + order.id()); + } + + public static Order getOrderById(UUID id) { + return ORDERS.get(id); + } +} + diff --git a/smithy-java-examples/quickstart-java/settings.gradle.kts b/smithy-java-examples/quickstart-java/settings.gradle.kts new file mode 100644 index 0000000..ec9961f --- /dev/null +++ b/smithy-java-examples/quickstart-java/settings.gradle.kts @@ -0,0 +1,21 @@ +rootProject.name = "smithy-java-quickstart" + +pluginManagement { + val smithyGradleVersion: String by settings + plugins { + id("software.amazon.smithy.gradle.smithy-jar").version(smithyGradleVersion) + id("software.amazon.smithy.gradle.smithy-base").version(smithyGradleVersion) + } + + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} + +// Subprojects +include("lib") +include("server") +include("client") +include("plugins") diff --git a/smithy-templates.json b/smithy-templates.json index 72c20ef..7d73a5b 100644 --- a/smithy-templates.json +++ b/smithy-templates.json @@ -139,6 +139,18 @@ "documentation": "Tutorial project that demonstrates how to use the Smithy to create a full-stack application.", "path": "tutorials/full-stack-application", "include": [".gitattributes"] + }, + "smithy-java-quickstart": { + "documentation": "Quickstart example for Smithy Java", + "path": "smithy-java-examples/quickstart-java", + "include": [ + "config/", + "gradle/", + "gradlew", + "gradlew.bat", + ".gitignore", + ".gitattributes" + ] } } } From a14e6fa06c2e65219f3ffe0cddc541f6c8dc7140 Mon Sep 17 00:00:00 2001 From: Hunter Mellema <124718352+hpmellema@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:00:31 -0700 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Hayden Baker --- smithy-java-examples/README.md | 2 +- smithy-java-examples/quickstart-java/README.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/smithy-java-examples/README.md b/smithy-java-examples/README.md index a8368cd..bc5a454 100644 --- a/smithy-java-examples/README.md +++ b/smithy-java-examples/README.md @@ -1,4 +1,4 @@ # Linting and Validation The examples in this directory demonstrate the use of the [Smithy Java](https://github.com/smithy-lang/smithy-java) code generator. -Additional examples cna be found [in the Smithy Java repository](https://github.com/smithy-lang/smithy-java/tree/main/examples). +Additional examples can be found [in the Smithy Java repository](https://github.com/smithy-lang/smithy-java/tree/main/examples). diff --git a/smithy-java-examples/quickstart-java/README.md b/smithy-java-examples/quickstart-java/README.md index 539950e..d7c4055 100644 --- a/smithy-java-examples/quickstart-java/README.md +++ b/smithy-java-examples/quickstart-java/README.md @@ -3,17 +3,17 @@ This project provides a template to get started using [Smithy Java](https://github.com/smithy-lang/smithy-java/) to create Java clients and servers. -For more information on this example see the [Smithy Java Quickstart Guide](https://smithy.io/2.0/java/quickstart.html). +For more information on this example, see the [Smithy Java Quickstart Guide](https://smithy.io/2.0/java/quickstart.html). ### Layout - `lib/`: Common package for the service API model. Shared by both client and server. - `server/`: Code generated Server that implements stubbed operations code-generated from the service model. - `client/`: Code generated client that can call the server. -- `plugins/`: a package defining client plugins. +- `plugins/`: A package defining client plugins. ### Usage -Then, to create a new project from this template, use the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) +To create a new project from this template, use the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) `init` command as follows: ```console @@ -25,6 +25,6 @@ smithy init -t smithy-java-quickstart To run and test the server, run `./gradlew run` from the root of a project created from this template. That will start the server running on port `8888`. -Once the server is running you can call the server using curl or by executing the integration tests in `client` subproject: +Once the server is running, you can call the server using curl or by executing the integration tests in `client` subproject: - `curl`: `curl -H "content-type: application/json" -d '{"coffeeType": "LATTE"}' -X POST localhost:8888/order` - integration tests: `./gradlew :client:integ`