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

feat(type-safe-api): add smithy shape library project for sharing smithy models between apis #875

Merged
merged 1 commit into from
Oct 29, 2024
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
Expand Up @@ -26,4 +26,4 @@ nav:
- "Integrations": websocket_integrations.md
- "Authorizers": websocket_authorizers.md
- "React Hooks": websocket_typescript_react_hooks.md
- "Troubleshooting": troubleshooting.md
- "FAQ": faq.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Troubleshooting
# FAQ

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

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

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

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

You can consume the library using the `addSmithyDeps` method, which adds a local file dependency to the built Smithy jar.
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.

=== "TS"

```ts
// Standalone model project, used as our model library
const shapes = new SmithyModelProject({
const shapes = new SmithyShapeLibraryProject({
name: "shapes",
parent: monorepo,
outdir: "packages/shapes",
smithyOptions: {
serviceName: {
namespace: "com.my.shared.shapes",
serviceName: "Ignored",
},
},
});

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

// Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
monorepo.addImplicitDependency(api.model.smithy!, shapes);

// Add a local file dependency on the built shapes jar
api.model.smithy!.definition.addSmithyDeps(shapes.definition);
// Add a dependency on the Smithy shape library
api.model.smithy!.addSmithyDeps(shapes);
```

=== "JAVA"

```java
// Standalone model project, used as our model library
SmithyModelProject shapes = SmithyModelProject.Builder.create()
SmithyShapeLibraryProject shapes = SmithyShapeLibraryProject.Builder.create()
.name("shapes")
.parent(monorepo)
.outdir("packages/shapes")
.smithyOptions(SmithyModelOptions.builder()
.serviceName(SmithyServiceName.builder()
.namespace("com.my.shared.shapes")
.serviceName("Ignored")
.build())
.build())
.build();

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

// Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
monorepo.addImplicitDependency(api.getModel().getSmithy(), shapes.getDefinition());

// Add a local file dependency on the built shapes jar
api.getModel().getSmithy().getDefinition().addSmithyDeps(shapes.getSmithy());
// Add a dependency on the Smithy shape library
api.getModel().getSmithy().addSmithyDeps(shapes);
```

=== "PYTHON"

```python
# Standalone model project, used as our model library
shapes = SmithyModelProject(
shapes = SmithyShapeLibraryProject(
name="shapes",
parent=monorepo,
outdir="packages/shapes",
smithy_options=SmithyModelOptions(
service_name=SmithyServiceName(
namespace="com.my.shared.shapes",
service_name="Ignored"
)
)
)

api = TypeSafeApiProject(...)

# Add the implicit monorepo dependency (if using the monorepo) to ensure the shape library is built before the api model
monorepo.add_implicit_dependency(api.model.smithy, shapes)

# Add a local file dependency on the built shapes jar
api.model.smithy.definition.add_smithy_deps(shapes.definition)
# Add a dependency on the Smithy shape library
api.model.smithy.add_smithy_deps(shapes)
```

### How do I debug my API locally?
Expand Down
5 changes: 3 additions & 2 deletions packages/type-safe-api/src/project/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ SPDX-License-Identifier: Apache-2.0 */
export * from "./model/type-safe-api-model-build";
export * from "./model/type-safe-api-async-model-build";
export * from "./model/smithy/smithy-project-definition";
export * from "./model/smithy/smithy-service-project-definition";
export * from "./model/openapi/open-api-project-definition";
export * from "./type-safe-api-project";
export * from "./model/type-safe-api-model-project";
export * from "./model/smithy/smithy-definition";
export * from "./model/openapi/open-api-definition";
export * from "./type-safe-websocket-api-project";
export * from "./model/type-safe-websocket-api-model-project";
export * from "./model/smithy/smithy-async-definition";
export * from "./model/openapi/open-api-async-definition";
export * from "./model/smithy/types";
export * from "./model/type-spec/type-spec-project-definition";
Expand All @@ -22,6 +21,8 @@ export * from "./model/smithy/smithy-async-model-project";
export * from "./model/smithy/smithy-model-project";
export * from "./model/type-spec/type-spec-model-project";
export * from "./model/type-spec/type-spec-async-model-project";
export * from "./model/smithy/smithy-shape-library-project";
export * from "./model/smithy/smithy-base-project";
export * from "./languages";
export * from "./types";
export * from "./typescript-project-options";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import { Project, SampleDir } from "projen";

/**
* Options for Smithy shape library sample code
*/
export interface SmithyShapeLibrarySampleCodeOptions {
/**
* Directory in which the smithy sample code should be written
*/
readonly modelDir: string;
/**
* Namespace
*/
readonly namespace: string;
}

/**
* Defines sample code for a Smithy shape library
*/
export class SmithyShapeLibrarySampleCode extends SampleDir {
constructor(
project: Project,
{ modelDir, namespace }: SmithyShapeLibrarySampleCodeOptions
) {
super(project, modelDir, {
files: {
"example.smithy": `$version: "2"
namespace ${namespace}

/// An example structure
structure Example {
@required
myProperty: String
}
`,
},
});
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0 */
import { Project, ProjectOptions } from "projen";
import {
SmithyAsyncDefinitionOptions,
SmithyAsyncDefinition,
} from "./smithy-async-definition";
import { TypeSafeApiCommandEnvironment } from "../../codegen/components/type-safe-api-command-environment";
import { ModelLanguage } from "../../types";
import { ModelReadme } from "../model-readme";
Expand All @@ -16,36 +11,103 @@ import {
TypeSafeApiModelBuild,
TypeSafeApiModelBuildOutputOptions,
} from "../type-safe-api-model-build";
import { SmithyAsyncSampleCode } from "./components/smithy-async-sample-code";
import { SmithyAwsPdkAsyncPrelude } from "./components/smithy-aws-pdk-async-prelude";
import {
SmithyBaseProject,
SmithyBaseProjectOptions,
} from "./smithy-base-project";
import { SmithyProjectDefinition } from "./smithy-project-definition";
import {
SmithyServiceProjectDefinition,
SmithyServiceProjectDefinitionOptions,
} from "./smithy-service-project-definition";
import { GenerateTask } from "../../codegen/components/generate-task";
import {
buildTypeSafeApiExecCommand,
TypeSafeApiScript,
} from "../../codegen/components/utils";

/**
* Options for the Smithy WebSocket API model project
*/
export interface SmithyAsyncModelProjectOptions
extends ProjectOptions,
SmithyAsyncDefinitionOptions,
extends SmithyBaseProjectOptions,
SmithyServiceProjectDefinitionOptions,
TypeSafeApiModelBuildOutputOptions,
TypeSafeApiAsyncModelBuildOutputOptions {}

/**
* Smithy model project for a WebSocket API
*/
export class SmithyAsyncModelProject extends Project {
export class SmithyAsyncModelProject extends SmithyBaseProject {
/**
* Name of the API
*/
public readonly apiName: string;
/**
* Smithy model and build settings
*/
public readonly definition: SmithyAsyncDefinition;
public readonly definition: SmithyServiceProjectDefinition;

constructor(options: SmithyAsyncModelProjectOptions) {
super(options);
TypeSafeApiCommandEnvironment.ensure(this);

this.definition = new SmithyAsyncDefinition(this, options);
this.definition = new SmithyServiceProjectDefinition(this, {
...options,
smithyOptions: {
...options.smithyOptions,
smithyBuildOptions: {
...options.smithyOptions.smithyBuildOptions,
projections: {
...options.smithyOptions.smithyBuildOptions?.projections,
openapi: {
...options.smithyOptions.smithyBuildOptions?.projections?.openapi,
transforms: [
// Add the async transform to the openapi projection
{ name: "aws-pdk-async-transformer", args: {} },
],
},
},
},
},
});
this.apiName = options.smithyOptions.serviceName.serviceName;

const { namespace: serviceNamespace, serviceName } =
options.smithyOptions.serviceName;
const firstHandlerLanguage = options.handlerLanguages?.[0];

// Create the default smithy model
new SmithyAsyncSampleCode(this, {
modelDir: this.definition.modelDir,
serviceName,
serviceNamespace,
firstHandlerLanguage,
});

// Add the additional async prelude
new SmithyAwsPdkAsyncPrelude(this, {
generatedModelDir: this.definition.generatedModelDir,
serviceNamespace,
handlerLanguages: options.handlerLanguages,
});

const generateTask = GenerateTask.ensure(this);

// Copy the async transformer jar
generateTask.prependExec(
buildTypeSafeApiExecCommand(
TypeSafeApiScript.COPY_ASYNC_SMITHY_TRANSFORMER
)
);

this.definition.addDeps(
`file://.smithy-async/aws-pdk-smithy-async-transformer.jar`
);
this.gitignore.addPatterns(".smithy-async");

new TypeSafeApiModelBuild(this, {
openApiSpecificationPath: this.definition.openApiSpecificationPath,
smithyJsonModelPath: this.definition.smithyJsonModelPath,
Expand All @@ -62,4 +124,8 @@ export class SmithyAsyncModelProject extends Project {
apiType: "async",
});
}

public smithyProjectDefinition(): SmithyProjectDefinition {
return this.definition;
}
}
Loading
Loading