Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Smithy CLI init command #1802

Merged
merged 1 commit into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package software.amazon.smithy.cli;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.utils.IoUtils;
import software.amazon.smithy.utils.ListUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;

public class InitCommandTest {
private static final String PROJECT_NAME = "smithy-templates";

@Test
public void init() {
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
setupTemplatesDirectory(templatesDir);

IntegUtils.withTempDir("exitZero", dir -> {
RunResult result = IntegUtils.run(
dir, ListUtils.of("init", "-t", "quickstart-cli", "-u", templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Smithy project created in directory: quickstart-cli"));
assertThat(result.getExitCode(), is(0));
assertThat(Files.exists(Paths.get(dir.toString(), "quickstart-cli")), is(true));
});
});
}

@Test
public void missingTemplate() {
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
setupTemplatesDirectory(templatesDir);

IntegUtils.withTempDir("missingTemplate", dir -> {
RunResult result = IntegUtils.run(
dir, ListUtils.of("init", "-u", templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Please specify a template using `--template` or `-t`"));
assertThat(result.getExitCode(), is(1));
});

IntegUtils.withTempDir("emptyTemplateName", dir -> {
RunResult result = IntegUtils.run(
dir, ListUtils.of("init", "-t", "", "-u", templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Please specify a template using `--template` or `-t`"));
assertThat(result.getExitCode(), is(1));
});
});
}

@Test
public void unexpectedTemplate() {
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
setupTemplatesDirectory(templatesDir);

IntegUtils.withTempDir("unexpectedTemplate", dir -> {
RunResult result = IntegUtils.run(
dir, ListUtils.of("init", "-t", "blabla", "-u", templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Missing expected member `blabla` from `templates` object ([3, 18])"));
assertThat(result.getExitCode(), is(1));
});
});
}

@Test
public void withDirectoryArg() {
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
setupTemplatesDirectory(templatesDir);

IntegUtils.withTempDir("withDirectoryArg", dir -> {
RunResult result = IntegUtils.run(dir, ListUtils.of(
"init", "-t", "quickstart-cli", "-o", "hello-world", "-u", templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Smithy project created in directory: hello-world"));
assertThat(result.getExitCode(), is(0));
assertThat(Files.exists(Paths.get(dir.toString(), "hello-world")), is(true));
});
});
}

@Test
public void withLongHandArgs() {
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
setupTemplatesDirectory(templatesDir);

IntegUtils.withTempDir("withLongHandArgs", dir -> {
RunResult result = IntegUtils.run(dir, ListUtils.of(
"init", "--template", "quickstart-cli", "--output", "hello-world", "--url",
templatesDir.toString()));
assertThat(result.getOutput(),
containsString("Smithy project created in directory: hello-world"));
assertThat(result.getExitCode(), is(0));
assertThat(Files.exists(Paths.get(dir.toString(), "hello-world")), is(true));
});
});
}

private static void run(List<String> args, Path root) {
StringBuilder output = new StringBuilder();
int result = IoUtils.runCommand(args, root, output, Collections.emptyMap());
if (result != 0) {
throw new RuntimeException("Error running command: " + args + ": " + output);
}
}

private void setupTemplatesDirectory(Path dir) {
run(ListUtils.of("git", "init"), dir);
run(ListUtils.of("git", "config", "user.email", "you@example.com"), dir);
run(ListUtils.of("git", "config", "user.name", "Your Name"), dir);
run(ListUtils.of("git", "checkout", "-b", "main"), dir);
run(ListUtils.of("git", "add", "-A"), dir);
run(ListUtils.of("git", "commit", "-m", "Foo"), dir);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
smithyVersion = 1.31.0

clean:
rm -rf weather-service/build/

build: clean
@echo Building getting-started-example...
(cd weather-service; smithy build)
@echo getting-started-example built successfully

test: build
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Getting started Example
This example provides a complete Smithy model for the Weather Service from the
[Smithy Quick Start Guide](https://smithy.io/2.0/quickstart.html).

## Using
To use this example as a template run the following.

```
smithy init --template quickstart-cli
```

*Note*: You will need the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) installed to use this command.
If you do not have the CLI installed, follow [this guide](https://smithy.io/2.0/guides/smithy-cli/index.html) to install it now.


## Building
To build this example run:
```
smithy build
```
From the root of this example directory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Quickstart Example
To run this example you will need the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) installed.
If you do not have the CLI installed, follow [this guide](https://smithy.io/2.0/guides/smithy-cli/index.html) to install it now.

Once you have the CLI installed run:
```
smithy build
```
From the root of this directory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
$version: "2"
namespace example.weather

/// Provides weather forecasts.
@paginated(
inputToken: "nextToken"
outputToken: "nextToken"
pageSize: "pageSize"
)
service Weather {
version: "2006-03-01"
resources: [City]
operations: [GetCurrentTime]
}

resource City {
identifiers: { cityId: CityId }
read: GetCity
list: ListCities
resources: [Forecast]
}

resource Forecast {
identifiers: { cityId: CityId }
read: GetForecast,
}

// "pattern" is a trait.
@pattern("^[A-Za-z0-9 ]+$")
string CityId

@readonly
operation GetCity {
input: GetCityInput
output: GetCityOutput
errors: [NoSuchResource]
}

@input
structure GetCityInput {
// "cityId" provides the identifier for the resource and
// has to be marked as required.
@required
cityId: CityId
}

@output
structure GetCityOutput {
// "required" is used on output to indicate if the service
// will always provide a value for the member.
@required
name: String

@required
coordinates: CityCoordinates
}

// This structure is nested within GetCityOutput.
structure CityCoordinates {
@required
latitude: Float

@required
longitude: Float
}

// "error" is a trait that is used to specialize
// a structure as an error.
@error("client")
structure NoSuchResource {
@required
resourceType: String
}

// The paginated trait indicates that the operation may
// return truncated results.
@readonly
@paginated(items: "items")
operation ListCities {
input: ListCitiesInput
output: ListCitiesOutput
}

@input
structure ListCitiesInput {
nextToken: String
pageSize: Integer
}

@output
structure ListCitiesOutput {
nextToken: String

@required
items: CitySummaries
}

// CitySummaries is a list of CitySummary structures.
list CitySummaries {
member: CitySummary
}

// CitySummary contains a reference to a City.
@references([{resource: City}])
structure CitySummary {
@required
cityId: CityId

@required
name: String
}

@readonly
operation GetCurrentTime {
input: GetCurrentTimeInput
output: GetCurrentTimeOutput
}

@input
structure GetCurrentTimeInput {}

@output
structure GetCurrentTimeOutput {
@required
time: Timestamp
}

@readonly
operation GetForecast {
input: GetForecastInput
output: GetForecastOutput
}

// "cityId" provides the only identifier for the resource since
// a Forecast doesn't have its own.
@input
structure GetForecastInput {
@required
cityId: CityId
}

@output
structure GetForecastOutput {
chanceOfRain: Float
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": "1.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Smithy-Examples",
"templates": {
"quickstart-cli": {
"documentation": "Smithy Quickstart example weather service using the Smithy CLI.",
"path": "getting-started-example/weather-service"
AndrewFossAWS marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Loading