Skip to content

Commit

Permalink
feat(open-api-gateway): idomatic smithy project structure for better …
Browse files Browse the repository at this point in the history
…ide support

This change restructures the smithy-based projects to combine the `model` and `smithy-build`
directories into a single `smithy` directory. This directory has a more similar structure to those
found in the Smithy documentation, and in turn means that IDE tools can be more easily configured to
help with the developer experience.

Specifically with this change, the Smithy IntelliJ plugin can be used to enhance the development
experience when authoring models. https://github.com/iancaffey/smithy-intellij-plugin

We preserve the behaviour of abstracting away the gradle wrapper, whereby it is ignored from the
generated project's source control by default, and copied from the PDK if it does not already
exist.  Similarly we allow users to bring their own `gradle wrapper` by choosing not to ignore
it in source control, and placing it in the `smithy` directory. This replaces the
`gradleWrapperPath` configuration option.

While this is a breaking change, the migration steps are fairly straightforward:

- Upgrade the open-api-gateway package
- `npx projen` to regenerate code - notice the new `smithy` folder in your api project
- Copy the contents of the old `model` directory into `smithy/src/main/smithy`
- If any custom dependencies were added to the `smithy-build/build.gradle` file, add them
to `smithy/build.gradle`
- Delete the old `model` and `smithy-build` directories

BREAKING CHANGE: Moved the location of the smithy model from 'model' to 'smithy/src/main/smithy'.
Removed the gradleWrapperPath configuration option.

fix #278
  • Loading branch information
cogwirrel committed Feb 3, 2023
1 parent 8be0798 commit a366643
Show file tree
Hide file tree
Showing 18 changed files with 13,912 additions and 5,259 deletions.
45 changes: 31 additions & 14 deletions packages/open-api-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ new SmithyApiGatewayTsProject({
This will result in a directory structure similar to the following:

```
|_ model/
|_ main.smithy - The Smithy model used to define your API. You can define as many .smithy files in here as you like.
|_ smithy-build/
|_ smithy/
|_ src/
|_ main/
|_ smithy - The Smithy model used to define your API. You can define as many .smithy files in here as you like.
|_ build.gradle - Smithy build gradle file - use this to add dependencies/plugins used in your smithy build
|_ settings.gradle - Setup for the Smithy gradle project
|_ smithy-build.json - Smithy build configuration - managed via options on the projen project
Expand Down Expand Up @@ -144,10 +145,11 @@ new SmithyApiGatewayTsProject({
// By default, the contents of `smithy-build/output` will be ignored by source control.
// Set this to false to include it, for example if you are generating clients directly from the smithy model.
ignoreSmithyBuildOutput: false,
// By default, the gradle wrapper is abstracted away in the PDK package itself.
// Set this to the path of a directory (relative to the outdir) where you have run `gradle wrapper`
// if you would like to use your own gradle wrapper and check this in to source control.
gradleWrapperPath: 'gradle',
// The gradle wrapper used for the smithy build is copied from the PDK itself if it does not already exist in
// the 'smithy' folder. By default, this gradle wrapper will be ignored by source control.
// Set this to false if you would like to check the gradle wrapper in to source control, for example if you want
// to use a different version of the gradle wrapper in your project.
ignoreGradleWrapper: false,
// Use smithyBuildOptions to control what is added to smithy-build.json. See more details below.
smithyBuildOptions: {
projections: {
Expand Down Expand Up @@ -186,15 +188,15 @@ smithyBuildOptions: {

The OpenAPI specification generated by this projection is used to create the CDK infrastructure, lambda handler wrappers, etc. Options for configuring OpenAPI generation can be found [in the Smithy OpenAPI documentation](https://awslabs.github.io/smithy/2.0/guides/converting-to-openapi.html).

Note that any additional dependencies required for projections/plugins can be added by modifying your `smithy-build/build.gradle` file:
Note that any additional dependencies required for projections/plugins can be added by modifying your `smithy/build.gradle` file:

```gradle
dependencies {
smithy("software.amazon.smithy:smithy-cli:1.24.0")
smithy("software.amazon.smithy:smithy-model:1.24.0")
smithy("software.amazon.smithy:smithy-openapi:1.24.0")
smithy("software.amazon.smithy:smithy-aws-traits:1.24.0")
smithy("other.dependency.example...")
implementation "software.amazon.smithy:smithy-cli:1.27.2"
implementation "software.amazon.smithy:smithy-model:1.27.2"
implementation "software.amazon.smithy:smithy-openapi:1.27.2"
implementation "software.amazon.smithy:smithy-aws-traits:1.27.2"
implementation "other.dependency.example..."
}
```

Expand Down Expand Up @@ -1284,4 +1286,19 @@ export class SampleApi extends Api {
}
```

You can remove the Web ACL entirely with `webAclOptions: { disable: true }` - you may wish to use this if you'd like to set up a Web ACL yourself with more control over the rules.
You can remove the Web ACL entirely with `webAclOptions: { disable: true }` - you may wish to use this if you'd like to set up a Web ACL yourself with more control over the rules.

#### Smithy IntelliJ Plugin

The Smithy-based projects are compatible with the [Smithy IntelliJ Plugin](https://github.com/iancaffey/smithy-intellij-plugin), which provides syntax highlighting and auto-complete for your Smithy model. To make use of it, perform the following steps:

* Install the "Smithy" plugin (under `Preferences -> Plugins`)
* Right-click on the `smithy/build.gradle` file in your Smithy API project
* Select "Link Gradle Project"

### Breaking Changes

* `v0.14.0` - see https://github.com/aws/aws-prototyping-sdk/pull/280
* Moved smithy model files from `model` directory to `smithy/src/main/smithy` - please move these manually as part of upgrading to `0.14.0`, and delete your `model` directory when done.
* Moved smithy gradle files from `smithy-build` directory to `smithy` - if you have added any dependencies to your `smithy-build/build.gradle` file you will need to copy them across into `smithy/build.gradle` (note dependencies in the new gradle file start with `implementation` rather than `smithy`).
* Deprecated `gradleWrapperPath` option on SmithApiGateway projects in favour of `ignoreGradleWrapper: false` - the gradle wrapper in `smithy` directory is always used (and generated automatically if not found). If you used a custom gradle wrapper, copy it into the `smithy` directory, set `ignoreGradleWrapper: false` and check it in to your repository.
50 changes: 30 additions & 20 deletions packages/open-api-gateway/samples/smithy/build.gradle
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
plugins {
id "software.amazon.smithy" version "0.6.0"
id "idea"
}

idea {
module {
sourceDirs += file("src/main/smithy")
}
}

repositories {
mavenLocal()
mavenCentral()
}

configurations {
smithy
}

// Add more dependencies here if you'd like to reference them in smithy-build.json
// Feel free to upgrade versions as appropriate
// Additional Smithy dependencies can be added here
dependencies {
smithy("software.amazon.smithy:smithy-cli:1.24.0")
smithy("software.amazon.smithy:smithy-model:1.24.0")
smithy("software.amazon.smithy:smithy-openapi:1.24.0")
smithy("software.amazon.smithy:smithy-aws-traits:1.24.0")
implementation "software.amazon.smithy:smithy-cli:1.27.2"
implementation "software.amazon.smithy:smithy-model:1.27.2"
implementation "software.amazon.smithy:smithy-openapi:1.27.2"
implementation "software.amazon.smithy:smithy-aws-traits:1.27.2"
}

// This task is called by the SmithyApiGateway project during project synthesis, modify at your own risk!
// This task is called by the SmithyApiGateway project and should not be removed!
task generate(type: JavaExec) {
classpath configurations.smithy
main = "software.amazon.smithy.cli.SmithyCli"
configurations.implementation.setCanBeResolved(true)

classpath = configurations.implementation
mainClass.set('software.amazon.smithy.cli.SmithyCli')

args("build",
"--config",
file(config).toString(),
"--output",
file(output).toString(),
"--discover",
file(discover).toString())
if (project.hasProperty("config") && project.hasProperty("output") && project.hasProperty("discover")) {
args("build",
"--config",
file(project.getProperty("config")).toString(),
"--output",
file(project.getProperty("output")).toString(),
"--discover",
file(project.getProperty("discover")).toString())
}
}
5 changes: 0 additions & 5 deletions packages/open-api-gateway/samples/smithy/settings.gradle

This file was deleted.

2 changes: 0 additions & 2 deletions packages/open-api-gateway/scripts/smithy/.gitignore

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import * as fs from "fs";
import * as path from "path";
import { Project, SampleFile, SampleFileOptions } from "projen";
import { getFilePermissions } from "projen/lib/util";

/**
* A sample file that is executable
*/
export class SampleExecutable extends SampleFile {
private readonly file: string;

constructor(project: Project, filePath: string, options: SampleFileOptions) {
super(project, filePath, options);
this.file = filePath;
}

/**
* @inheritDoc
*/
public synthesize() {
const fullFilePath = path.join(this.project.outdir, this.file);
if (fs.existsSync(fullFilePath)) {
return;
}

super.synthesize();

fs.chmodSync(
fullFilePath,
getFilePermissions({
executable: true,
readonly: false,
})
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import * as path from "path";
import { Component, Project } from "projen";
import { exec } from "projen/lib/util";

Expand All @@ -19,19 +18,14 @@ export interface SmithyGeneratedOpenApiSpecOptions {
readonly smithyBuildConfigPath: string;

/**
* Absolute to smithy build output
* Absolute path to smithy build output
*/
readonly outputPath: string;

/**
* Absolute path to the gradle project with the "generate" task
*/
readonly gradleProjectPath: string;

/**
* Custom gradle wrapper path
*/
readonly gradleWrapperPath?: string;
}

/**
Expand All @@ -48,21 +42,14 @@ export class SmithyGeneratedOutput extends Component {
synthesize() {
super.synthesize();

const {
smithyBuildConfigPath,
modelPath,
outputPath,
gradleProjectPath,
gradleWrapperPath,
} = this.options;
const { smithyBuildConfigPath, modelPath, outputPath, gradleProjectPath } =
this.options;

// Run smithy generation
exec(
`./gradlew -p ${gradleProjectPath} generate -Pconfig=${smithyBuildConfigPath} -Pdiscover=${modelPath} -Poutput=${outputPath}`,
{
cwd:
gradleWrapperPath ??
path.resolve(__dirname, "..", "..", "..", "..", "scripts", "smithy"),
cwd: gradleProjectPath,
}
);
}
Expand Down
30 changes: 10 additions & 20 deletions packages/open-api-gateway/src/project/smithy/setup-smithy-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ SPDX-License-Identifier: Apache-2.0 */
import * as path from "path";
import { Project } from "projen";
import { SmithyBuildProject } from "./smithy-build-project";
import { SmithyModelProject } from "./smithy-model-project";
import { SmithyApiGatewayProjectOptions } from "../types";

/**
Expand All @@ -28,35 +27,19 @@ export const setupSmithyBuild = (
options: SmithyApiGatewayProjectOptions
): SmithyBuildProjectResult => {
const modelDir = options.modelDir ?? "model";
const { namespace: serviceNamespace, serviceName } = options.serviceName;
const fullyQualifiedServiceName = `${serviceNamespace}#${serviceName}`;

const smithyBuildDir = "smithy-build";

// Create a smithy model (eg main.smithy)
const smithyModel = new SmithyModelProject({
name: `${project.name}-smithy`,
outdir: modelDir,
parent: project,
serviceNamespace,
serviceName,
});
smithyModel.synth();
const { serviceName } = options.serviceName;

const smithyBuildDir = "smithy";
const smithyBuildOutputSubDir = "output";

// Create the smithy build project, responsible for transforming the model into an OpenAPI spec
const smithyBuild = new SmithyBuildProject({
name: `${project.name}-smithy-build`,
parent: project,
outdir: smithyBuildDir,
fullyQualifiedServiceName,
serviceName: options.serviceName,
smithyBuildOptions: options.smithyBuildOptions,
modelPath: path.join(project.outdir, modelDir),
buildOutputDir: smithyBuildOutputSubDir,
gradleWrapperPath: options.gradleWrapperPath
? path.resolve(project.outdir, options.gradleWrapperPath)
: undefined,
});
smithyBuild.synth();

Expand All @@ -69,8 +52,15 @@ export const setupSmithyBuild = (
if (options.ignoreSmithyBuildOutput ?? true) {
project.gitignore.addPatterns(smithyBuildOutputDir);
}
// Ignore gradle wrapper by default
if (options.ignoreGradleWrapper ?? true) {
project.gitignore.addPatterns(path.join(smithyBuildDir, "gradle"));
project.gitignore.addPatterns(path.join(smithyBuildDir, "gradlew"));
project.gitignore.addPatterns(path.join(smithyBuildDir, "gradlew.bat"));
}
// Ignore the .gradle directory
project.gitignore.addPatterns(path.join(smithyBuildDir, ".gradle"));
project.gitignore.addPatterns(path.join(smithyBuildDir, "build"));

return {
modelDir,
Expand Down
Loading

0 comments on commit a366643

Please sign in to comment.