Skip to content

Commit 37b4aa2

Browse files
authored
feat(type-safe-api): add smithy shape library project for sharing smithy models between apis (#875)
Adds a `SmithyShapeLibraryProject` which can be used to define Smithy models that can be reused in different APIs. Previously, one would have to declare a `SmithyModelProject` to achieve this, but this needed an ignored `service` to be defined in the model and generated an OpenAPI spec as part of its build, which is unnecessary. Fixes #825
1 parent c6d5b4c commit 37b4aa2

19 files changed

+5387
-306
lines changed

packages/type-safe-api/docs/developer_guides/type-safe-api/.pages.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ nav:
2626
- "Integrations": websocket_integrations.md
2727
- "Authorizers": websocket_authorizers.md
2828
- "React Hooks": websocket_typescript_react_hooks.md
29-
- "Troubleshooting": troubleshooting.md
29+
- "FAQ": faq.md

packages/type-safe-api/docs/developer_guides/type-safe-api/troubleshooting.md packages/type-safe-api/docs/developer_guides/type-safe-api/faq.md

+12-39
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Troubleshooting
1+
# FAQ
22

33
### Can I write my CDK code in Java if my .projenrc is in TypeScript?
44

@@ -112,83 +112,56 @@ If you would like to introduce tags without breaking existing clients, we recomm
112112

113113
### I have multiple Smithy-based APIs, can they share common structures?
114114

115-
Yes. You can create a `SmithyModelProject` on its own to create a standalone Smithy model library, which can contain the shared structures.
115+
Yes. You can create a `SmithyShapeLibraryProject` on its own to create a standalone Smithy model library, which can contain the shared structures.
116116

117-
You can consume the library using the `addSmithyDeps` method, which adds a local file dependency to the built Smithy jar.
117+
You can consume the library using the `addSmithyDeps` method, which adds a local file dependency to the built Smithy jar, as well as setting up an implicit dependency in the monorepo if used.
118118

119119
=== "TS"
120120

121121
```ts
122122
// Standalone model project, used as our model library
123-
const shapes = new SmithyModelProject({
123+
const shapes = new SmithyShapeLibraryProject({
124124
name: "shapes",
125125
parent: monorepo,
126126
outdir: "packages/shapes",
127-
smithyOptions: {
128-
serviceName: {
129-
namespace: "com.my.shared.shapes",
130-
serviceName: "Ignored",
131-
},
132-
},
133127
});
134128

135129
const api = new TypeSafeApiProject({ ... });
136130

137-
// Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
138-
monorepo.addImplicitDependency(api.model.smithy!, shapes);
139-
140-
// Add a local file dependency on the built shapes jar
141-
api.model.smithy!.definition.addSmithyDeps(shapes.definition);
131+
// Add a dependency on the Smithy shape library
132+
api.model.smithy!.addSmithyDeps(shapes);
142133
```
143134

144135
=== "JAVA"
145136

146137
```java
147138
// Standalone model project, used as our model library
148-
SmithyModelProject shapes = SmithyModelProject.Builder.create()
139+
SmithyShapeLibraryProject shapes = SmithyShapeLibraryProject.Builder.create()
149140
.name("shapes")
150141
.parent(monorepo)
151142
.outdir("packages/shapes")
152-
.smithyOptions(SmithyModelOptions.builder()
153-
.serviceName(SmithyServiceName.builder()
154-
.namespace("com.my.shared.shapes")
155-
.serviceName("Ignored")
156-
.build())
157-
.build())
158143
.build();
159144

160145
TypeSafeApiProject api = new TypeSafeApiProject(TypeSafeApiProjectOptions.builder()....build();
161146

162-
// Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
163-
monorepo.addImplicitDependency(api.getModel().getSmithy(), shapes.getDefinition());
164-
165-
// Add a local file dependency on the built shapes jar
166-
api.getModel().getSmithy().getDefinition().addSmithyDeps(shapes.getSmithy());
147+
// Add a dependency on the Smithy shape library
148+
api.getModel().getSmithy().addSmithyDeps(shapes);
167149
```
168150

169151
=== "PYTHON"
170152

171153
```python
172154
# Standalone model project, used as our model library
173-
shapes = SmithyModelProject(
155+
shapes = SmithyShapeLibraryProject(
174156
name="shapes",
175157
parent=monorepo,
176158
outdir="packages/shapes",
177-
smithy_options=SmithyModelOptions(
178-
service_name=SmithyServiceName(
179-
namespace="com.my.shared.shapes",
180-
service_name="Ignored"
181-
)
182-
)
183159
)
184160

185161
api = TypeSafeApiProject(...)
186162

187-
# Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
188-
monorepo.add_implicit_dependency(api.model.smithy, shapes)
189-
190-
# Add a local file dependency on the built shapes jar
191-
api.model.smithy.definition.add_smithy_deps(shapes.definition)
163+
# Add a dependency on the Smithy shape library
164+
api.model.smithy.add_smithy_deps(shapes)
192165
```
193166

194167
### How do I debug my API locally?

packages/type-safe-api/src/project/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ SPDX-License-Identifier: Apache-2.0 */
33
export * from "./model/type-safe-api-model-build";
44
export * from "./model/type-safe-api-async-model-build";
55
export * from "./model/smithy/smithy-project-definition";
6+
export * from "./model/smithy/smithy-service-project-definition";
67
export * from "./model/openapi/open-api-project-definition";
78
export * from "./type-safe-api-project";
89
export * from "./model/type-safe-api-model-project";
9-
export * from "./model/smithy/smithy-definition";
1010
export * from "./model/openapi/open-api-definition";
1111
export * from "./type-safe-websocket-api-project";
1212
export * from "./model/type-safe-websocket-api-model-project";
13-
export * from "./model/smithy/smithy-async-definition";
1413
export * from "./model/openapi/open-api-async-definition";
1514
export * from "./model/smithy/types";
1615
export * from "./model/type-spec/type-spec-project-definition";
@@ -22,6 +21,8 @@ export * from "./model/smithy/smithy-async-model-project";
2221
export * from "./model/smithy/smithy-model-project";
2322
export * from "./model/type-spec/type-spec-model-project";
2423
export * from "./model/type-spec/type-spec-async-model-project";
24+
export * from "./model/smithy/smithy-shape-library-project";
25+
export * from "./model/smithy/smithy-base-project";
2526
export * from "./languages";
2627
export * from "./types";
2728
export * from "./typescript-project-options";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
2+
SPDX-License-Identifier: Apache-2.0 */
3+
import { Project, SampleDir } from "projen";
4+
5+
/**
6+
* Options for Smithy shape library sample code
7+
*/
8+
export interface SmithyShapeLibrarySampleCodeOptions {
9+
/**
10+
* Directory in which the smithy sample code should be written
11+
*/
12+
readonly modelDir: string;
13+
/**
14+
* Namespace
15+
*/
16+
readonly namespace: string;
17+
}
18+
19+
/**
20+
* Defines sample code for a Smithy shape library
21+
*/
22+
export class SmithyShapeLibrarySampleCode extends SampleDir {
23+
constructor(
24+
project: Project,
25+
{ modelDir, namespace }: SmithyShapeLibrarySampleCodeOptions
26+
) {
27+
super(project, modelDir, {
28+
files: {
29+
"example.smithy": `$version: "2"
30+
namespace ${namespace}
31+
32+
/// An example structure
33+
structure Example {
34+
@required
35+
myProperty: String
36+
}
37+
`,
38+
},
39+
});
40+
}
41+
}

packages/type-safe-api/src/project/model/smithy/smithy-async-definition.ts

-78
This file was deleted.

packages/type-safe-api/src/project/model/smithy/smithy-async-model-project.ts

+76-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
22
SPDX-License-Identifier: Apache-2.0 */
3-
import { Project, ProjectOptions } from "projen";
4-
import {
5-
SmithyAsyncDefinitionOptions,
6-
SmithyAsyncDefinition,
7-
} from "./smithy-async-definition";
83
import { TypeSafeApiCommandEnvironment } from "../../codegen/components/type-safe-api-command-environment";
94
import { ModelLanguage } from "../../types";
105
import { ModelReadme } from "../model-readme";
@@ -16,36 +11,103 @@ import {
1611
TypeSafeApiModelBuild,
1712
TypeSafeApiModelBuildOutputOptions,
1813
} from "../type-safe-api-model-build";
14+
import { SmithyAsyncSampleCode } from "./components/smithy-async-sample-code";
15+
import { SmithyAwsPdkAsyncPrelude } from "./components/smithy-aws-pdk-async-prelude";
16+
import {
17+
SmithyBaseProject,
18+
SmithyBaseProjectOptions,
19+
} from "./smithy-base-project";
20+
import { SmithyProjectDefinition } from "./smithy-project-definition";
21+
import {
22+
SmithyServiceProjectDefinition,
23+
SmithyServiceProjectDefinitionOptions,
24+
} from "./smithy-service-project-definition";
25+
import { GenerateTask } from "../../codegen/components/generate-task";
26+
import {
27+
buildTypeSafeApiExecCommand,
28+
TypeSafeApiScript,
29+
} from "../../codegen/components/utils";
1930

2031
/**
2132
* Options for the Smithy WebSocket API model project
2233
*/
2334
export interface SmithyAsyncModelProjectOptions
24-
extends ProjectOptions,
25-
SmithyAsyncDefinitionOptions,
35+
extends SmithyBaseProjectOptions,
36+
SmithyServiceProjectDefinitionOptions,
2637
TypeSafeApiModelBuildOutputOptions,
2738
TypeSafeApiAsyncModelBuildOutputOptions {}
2839

2940
/**
3041
* Smithy model project for a WebSocket API
3142
*/
32-
export class SmithyAsyncModelProject extends Project {
43+
export class SmithyAsyncModelProject extends SmithyBaseProject {
3344
/**
3445
* Name of the API
3546
*/
3647
public readonly apiName: string;
3748
/**
3849
* Smithy model and build settings
3950
*/
40-
public readonly definition: SmithyAsyncDefinition;
51+
public readonly definition: SmithyServiceProjectDefinition;
4152

4253
constructor(options: SmithyAsyncModelProjectOptions) {
4354
super(options);
4455
TypeSafeApiCommandEnvironment.ensure(this);
4556

46-
this.definition = new SmithyAsyncDefinition(this, options);
57+
this.definition = new SmithyServiceProjectDefinition(this, {
58+
...options,
59+
smithyOptions: {
60+
...options.smithyOptions,
61+
smithyBuildOptions: {
62+
...options.smithyOptions.smithyBuildOptions,
63+
projections: {
64+
...options.smithyOptions.smithyBuildOptions?.projections,
65+
openapi: {
66+
...options.smithyOptions.smithyBuildOptions?.projections?.openapi,
67+
transforms: [
68+
// Add the async transform to the openapi projection
69+
{ name: "aws-pdk-async-transformer", args: {} },
70+
],
71+
},
72+
},
73+
},
74+
},
75+
});
4776
this.apiName = options.smithyOptions.serviceName.serviceName;
4877

78+
const { namespace: serviceNamespace, serviceName } =
79+
options.smithyOptions.serviceName;
80+
const firstHandlerLanguage = options.handlerLanguages?.[0];
81+
82+
// Create the default smithy model
83+
new SmithyAsyncSampleCode(this, {
84+
modelDir: this.definition.modelDir,
85+
serviceName,
86+
serviceNamespace,
87+
firstHandlerLanguage,
88+
});
89+
90+
// Add the additional async prelude
91+
new SmithyAwsPdkAsyncPrelude(this, {
92+
generatedModelDir: this.definition.generatedModelDir,
93+
serviceNamespace,
94+
handlerLanguages: options.handlerLanguages,
95+
});
96+
97+
const generateTask = GenerateTask.ensure(this);
98+
99+
// Copy the async transformer jar
100+
generateTask.prependExec(
101+
buildTypeSafeApiExecCommand(
102+
TypeSafeApiScript.COPY_ASYNC_SMITHY_TRANSFORMER
103+
)
104+
);
105+
106+
this.definition.addDeps(
107+
`file://.smithy-async/aws-pdk-smithy-async-transformer.jar`
108+
);
109+
this.gitignore.addPatterns(".smithy-async");
110+
49111
new TypeSafeApiModelBuild(this, {
50112
openApiSpecificationPath: this.definition.openApiSpecificationPath,
51113
smithyJsonModelPath: this.definition.smithyJsonModelPath,
@@ -62,4 +124,8 @@ export class SmithyAsyncModelProject extends Project {
62124
apiType: "async",
63125
});
64126
}
127+
128+
public smithyProjectDefinition(): SmithyProjectDefinition {
129+
return this.definition;
130+
}
65131
}

0 commit comments

Comments
 (0)