Skip to content

Commit d7c92e8

Browse files
committed
Add Smithy CLI init command
1 parent ad6d20b commit d7c92e8

File tree

10 files changed

+511
-1
lines changed

10 files changed

+511
-1
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package software.amazon.smithy.cli;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.containsString;
5+
import static org.hamcrest.Matchers.is;
6+
import org.junit.jupiter.api.Test;
7+
import software.amazon.smithy.utils.IoUtils;
8+
import software.amazon.smithy.utils.ListUtils;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.nio.file.Paths;
12+
import java.util.Collections;
13+
import java.util.List;
14+
15+
public class InitCommandTest {
16+
private static final String PROJECT_NAME = "smithy-templates";
17+
18+
@Test
19+
public void init() {
20+
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
21+
setupTemplatesDirectory(templatesDir);
22+
23+
IntegUtils.withTempDir("exitZero", dir -> {
24+
RunResult result = IntegUtils.run(
25+
dir, ListUtils.of("init", "-t", "quickstart-cli", "-u", templatesDir.toString()));
26+
assertThat(result.getOutput(),
27+
containsString("Smithy project created in directory: quickstart-cli"));
28+
assertThat(result.getExitCode(), is(0));
29+
assertThat(Files.exists(Paths.get(dir.toString(), "quickstart-cli")), is(true));
30+
});
31+
});
32+
}
33+
34+
@Test
35+
public void missingTemplate() {
36+
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
37+
setupTemplatesDirectory(templatesDir);
38+
39+
IntegUtils.withTempDir("missingTemplate", dir -> {
40+
RunResult result = IntegUtils.run(
41+
dir, ListUtils.of("init", "-u", templatesDir.toString()));
42+
assertThat(result.getOutput(),
43+
containsString("Please specify a template using `--template` or `-t`"));
44+
assertThat(result.getExitCode(), is(1));
45+
});
46+
47+
IntegUtils.withTempDir("emptyTemplateName", dir -> {
48+
RunResult result = IntegUtils.run(
49+
dir, ListUtils.of("init", "-t", "", "-u", templatesDir.toString()));
50+
assertThat(result.getOutput(),
51+
containsString("Please specify a template using `--template` or `-t`"));
52+
assertThat(result.getExitCode(), is(1));
53+
});
54+
});
55+
}
56+
57+
@Test
58+
public void unexpectedTemplate() {
59+
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
60+
setupTemplatesDirectory(templatesDir);
61+
62+
IntegUtils.withTempDir("unexpectedTemplate", dir -> {
63+
RunResult result = IntegUtils.run(
64+
dir, ListUtils.of("init", "-t", "blabla", "-u", templatesDir.toString()));
65+
assertThat(result.getOutput(),
66+
containsString("Missing expected member `blabla` from `templates` object ([3, 18])"));
67+
assertThat(result.getExitCode(), is(1));
68+
});
69+
});
70+
}
71+
72+
@Test
73+
public void withDirectoryArg() {
74+
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
75+
setupTemplatesDirectory(templatesDir);
76+
77+
IntegUtils.withTempDir("withDirectoryArg", dir -> {
78+
RunResult result = IntegUtils.run(dir, ListUtils.of(
79+
"init", "-t", "quickstart-cli", "-o", "hello-world", "-u", templatesDir.toString()));
80+
assertThat(result.getOutput(),
81+
containsString("Smithy project created in directory: hello-world"));
82+
assertThat(result.getExitCode(), is(0));
83+
assertThat(Files.exists(Paths.get(dir.toString(), "hello-world")), is(true));
84+
});
85+
});
86+
}
87+
88+
@Test
89+
public void withLongHandArgs() {
90+
IntegUtils.withProject(PROJECT_NAME, templatesDir -> {
91+
setupTemplatesDirectory(templatesDir);
92+
93+
IntegUtils.withTempDir("withLongHandArgs", dir -> {
94+
RunResult result = IntegUtils.run(dir, ListUtils.of(
95+
"init", "--template", "quickstart-cli", "--output", "hello-world", "--url",
96+
templatesDir.toString()));
97+
assertThat(result.getOutput(),
98+
containsString("Smithy project created in directory: hello-world"));
99+
assertThat(result.getExitCode(), is(0));
100+
assertThat(Files.exists(Paths.get(dir.toString(), "hello-world")), is(true));
101+
});
102+
});
103+
}
104+
105+
private static void run(List<String> args, Path root) {
106+
StringBuilder output = new StringBuilder();
107+
int result = IoUtils.runCommand(args, root, output, Collections.emptyMap());
108+
if (result != 0) {
109+
throw new RuntimeException("Error running command: " + args + ": " + output);
110+
}
111+
}
112+
113+
private void setupTemplatesDirectory(Path dir) {
114+
run(ListUtils.of("git", "init"), dir);
115+
run(ListUtils.of("git", "config", "user.email", "you@example.com"), dir);
116+
run(ListUtils.of("git", "config", "user.name", "Your Name"), dir);
117+
run(ListUtils.of("git", "checkout", "-b", "main"), dir);
118+
run(ListUtils.of("git", "add", "-A"), dir);
119+
run(ListUtils.of("git", "commit", "-m", "Foo"), dir);
120+
}
121+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
smithyVersion = 1.31.0
2+
3+
clean:
4+
rm -rf weather-service/build/
5+
6+
build: clean
7+
@echo Building getting-started-example...
8+
(cd weather-service; smithy build)
9+
@echo getting-started-example built successfully
10+
11+
test: build
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Getting started Example
2+
This example provides a complete Smithy model for the Weather Service from the
3+
[Smithy Quick Start Guide](https://smithy.io/2.0/quickstart.html).
4+
5+
## Using
6+
To use this example as a template run the following.
7+
8+
```
9+
smithy init --template quickstart-cli
10+
```
11+
12+
*Note*: You will need the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) installed to use this command.
13+
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.
14+
15+
16+
## Building
17+
To build this example run:
18+
```
19+
smithy build
20+
```
21+
From the root of this example directory.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Quickstart Example
2+
To run this example you will need the [Smithy CLI](https://smithy.io/2.0/guides/smithy-cli/index.html) installed.
3+
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.
4+
5+
Once you have the CLI installed run:
6+
```
7+
smithy build
8+
```
9+
From the root of this directory.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
$version: "2"
2+
namespace example.weather
3+
4+
/// Provides weather forecasts.
5+
@paginated(
6+
inputToken: "nextToken"
7+
outputToken: "nextToken"
8+
pageSize: "pageSize"
9+
)
10+
service Weather {
11+
version: "2006-03-01"
12+
resources: [City]
13+
operations: [GetCurrentTime]
14+
}
15+
16+
resource City {
17+
identifiers: { cityId: CityId }
18+
read: GetCity
19+
list: ListCities
20+
resources: [Forecast]
21+
}
22+
23+
resource Forecast {
24+
identifiers: { cityId: CityId }
25+
read: GetForecast,
26+
}
27+
28+
// "pattern" is a trait.
29+
@pattern("^[A-Za-z0-9 ]+$")
30+
string CityId
31+
32+
@readonly
33+
operation GetCity {
34+
input: GetCityInput
35+
output: GetCityOutput
36+
errors: [NoSuchResource]
37+
}
38+
39+
@input
40+
structure GetCityInput {
41+
// "cityId" provides the identifier for the resource and
42+
// has to be marked as required.
43+
@required
44+
cityId: CityId
45+
}
46+
47+
@output
48+
structure GetCityOutput {
49+
// "required" is used on output to indicate if the service
50+
// will always provide a value for the member.
51+
@required
52+
name: String
53+
54+
@required
55+
coordinates: CityCoordinates
56+
}
57+
58+
// This structure is nested within GetCityOutput.
59+
structure CityCoordinates {
60+
@required
61+
latitude: Float
62+
63+
@required
64+
longitude: Float
65+
}
66+
67+
// "error" is a trait that is used to specialize
68+
// a structure as an error.
69+
@error("client")
70+
structure NoSuchResource {
71+
@required
72+
resourceType: String
73+
}
74+
75+
// The paginated trait indicates that the operation may
76+
// return truncated results.
77+
@readonly
78+
@paginated(items: "items")
79+
operation ListCities {
80+
input: ListCitiesInput
81+
output: ListCitiesOutput
82+
}
83+
84+
@input
85+
structure ListCitiesInput {
86+
nextToken: String
87+
pageSize: Integer
88+
}
89+
90+
@output
91+
structure ListCitiesOutput {
92+
nextToken: String
93+
94+
@required
95+
items: CitySummaries
96+
}
97+
98+
// CitySummaries is a list of CitySummary structures.
99+
list CitySummaries {
100+
member: CitySummary
101+
}
102+
103+
// CitySummary contains a reference to a City.
104+
@references([{resource: City}])
105+
structure CitySummary {
106+
@required
107+
cityId: CityId
108+
109+
@required
110+
name: String
111+
}
112+
113+
@readonly
114+
operation GetCurrentTime {
115+
input: GetCurrentTimeInput
116+
output: GetCurrentTimeOutput
117+
}
118+
119+
@input
120+
structure GetCurrentTimeInput {}
121+
122+
@output
123+
structure GetCurrentTimeOutput {
124+
@required
125+
time: Timestamp
126+
}
127+
128+
@readonly
129+
operation GetForecast {
130+
input: GetForecastInput
131+
output: GetForecastOutput
132+
}
133+
134+
// "cityId" provides the only identifier for the resource since
135+
// a Forecast doesn't have its own.
136+
@input
137+
structure GetForecastInput {
138+
@required
139+
cityId: CityId
140+
}
141+
142+
@output
143+
structure GetForecastOutput {
144+
chanceOfRain: Float
145+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"version": "1.0"
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "Smithy-Examples",
3+
"templates": {
4+
"quickstart-cli": {
5+
"documentation": "Smithy Quickstart example weather service using the Smithy CLI.",
6+
"path": "getting-started-example/weather-service"
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)