diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be209e981..b220cd079 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -192,5 +192,5 @@ jobs: touch .nojekyll git add . git diff --cached --exit-code >/dev/null || (git commit -am 'docs: publish from ${{ github.sha }}') - # - name: Push - # run: git push origin gh-pages:gh-pages + - name: Push + run: git push origin gh-pages:gh-pages diff --git a/README.md b/README.md index 97fa6b1fe..6496255c6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,131 @@ -# aws-pdk + + +# Getting started + +## What is the AWS PDK? + +The AWS Project Development Kit (AWS PDK) provides building blocks for common patterns together with development tools to manage and build your projects. + +The AWS PDK lets you define your projects programatically via the expressive power of type safe constructs available in one of 3 languages (typescript, python or java). This approach yields many benefits, including: + +- Ability to set up new projects within seconds, with all boilerplate already pre-configured. +- Receive updates to previously bootstrapped projects when new versions become available i.e: updated dependenies or lint configurations. +- Build polyglot monorepos, with build caching, cross-language build dependencies, dependency visualization and much more. +- Leverage codified patterns which vend project and infrastructure (CDK) code. + +The AWS PDK is built on top of [Projen](https://github.com/projen/projen) and as such all constructs that you compose together need to be defined via a [projenrc](https://projen.io/programmatic-api.html) file. + +## Why use the AWS PDK? + +It's much easier to show than explain! Here is some PDK code (within projenrc file) that creates a Polyglot monorepo, with a React Website pre-configured with Cognito Auth and pre-integrated with a Smithy Type Safe Api. + +```ts +import { CloudscapeReactTsWebsiteProject } from "@aws/pdk/cloudscape-react-ts-website"; +import { InfrastructureTsProject } from "@aws/pdk/infrastructure"; +import { MonorepoTsProject } from "@aws/pdk/monorepo"; +import { + DocumentationFormat, + Language, + Library, + ModelLanguage, + TypeSafeApiProject, +} from "@aws/pdk/type-safe-api"; +import { javascript } from "projen"; + +const monorepo = new MonorepoTsProject({ + name: "my-project", + packageManager: javascript.NodePackageManager.PNPM, + projenrcTs: true, +}); + +const api = new TypeSafeApiProject({ + parent: monorepo, + outdir: "packages/api", + name: "myapi", + infrastructure: { + language: Language.TYPESCRIPT, + }, + model: { + language: ModelLanguage.SMITHY, + options: { + smithy: { + serviceName: { + namespace: "com.aws", + serviceName: "MyApi", + }, + }, + }, + }, + runtime: { + languages: [Language.TYPESCRIPT], + }, + documentation: { + formats: [DocumentationFormat.HTML_REDOC], + }, + library: { + libraries: [Library.TYPESCRIPT_REACT_QUERY_HOOKS], + }, + handlers: { + languages: [Language.TYPESCRIPT], + }, +}); + +const website = new CloudscapeReactTsWebsiteProject({ + parent: monorepo, + outdir: "packages/website", + name: "website", + typeSafeApi: api, +}); + +new InfrastructureTsProject({ + parent: monorepo, + outdir: "packages/infra", + name: "infra", + cloudscapeReactTsWebsite: website, + typeSafeApi: api, +}); + +monorepo.synth(); +``` + +This code (also available in Python and Java), produces all the source code, packages and infrastructure needed to deploy a fully-operable application in the AWS cloud. All that's left to do is build and deploy it! + +From this ~70 lines of code above, the AWS PDK produces the following packages on your behalf: + +- `monorepo`: Root level project that manages interdependencies between projects within the Monorepo, provides build caching and dependency visualziation. +- `api/model`: A project that allows you to define your API using Smithy (or OpenAPI) IDL. +- `api/generated/documentation`: A project that automatically creates API documentation in a variety of formats. +- `api/generated/infrastructure`: A project that automatically creates API infrastructure constructs in a type-safe manner. +- `api/generated/libraries`: A project that automatically generates a react hooks library that can be used to call your API from a React based website. +- `api/generated/runtime`: A project that contains server bindings for handlers to ensure type safety. +- `api/handlers`: A project that automatically creates handler stubs, preconfigured with type-safety and a variety of value added features based on your defined API's. +- `website`: A project which creates a React based website built using [Cloudscape](https://cloudscape.design/) that comes pre-integrated with Cognito Auth and your created API. This provides you with the ability to call your API securely. +- `infra`: A project which sets up all CDK related infrastructure needed to deploy your application. It also comes pre-configured to generate a diagram based on your CDK code everytime you build. + +### Bootstrapped Source + + + +### Generated Website + + + +### Generated Diagram + + + +As you can see, the AWS PDK provides you with valuable time savings so you can focus on working on what matters most to your project. + +## Developing with the AWS PDK + +Please refer to the full documentation website. https://aws.github.io/aws-pdk +## Contributing to the AWS PDK + +https://aws.github.io/aws-pdk/contributing/index.html + ## License This project is licensed under the Apache-2.0 License. diff --git a/docs/content/assets/images/rebrand_banner.png b/docs/content/assets/images/rebrand_banner.png new file mode 100644 index 000000000..8378f6e2b Binary files /dev/null and b/docs/content/assets/images/rebrand_banner.png differ diff --git a/docs/content/contributing/index.md b/docs/content/contributing/index.md index 4fb43b749..188491ffb 100644 --- a/docs/content/contributing/index.md +++ b/docs/content/contributing/index.md @@ -16,7 +16,8 @@ This is where the documentation site is defined and built. Contains configurations for each project within this monorepo. -**_As a rule of thumb, every package in the monorepo should correspond to a file in this directory._** +!!!info + As a rule of thumb, every package in the monorepo should correspond to a file in this directory. ### **packages** @@ -214,7 +215,7 @@ From the root directory run: `pdk upgrade-deps`. This will bump all dependencies If you run into an issue that resembles: ```bash -Type 'import(".../@aws/pdk/node_modules/aws-prototyping-sdk/node_modules/projen/lib/ignore-file").IgnoreFile' is not assignable to type 'import(".../@aws/pdk/node_modules/projen/lib/ignore-file").IgnoreFile'. +Type 'import(".../@aws/pdk/node_modules/@aws/pdk/node_modules/projen/lib/ignore-file").IgnoreFile' is not assignable to type 'import(".../@aws/pdk/node_modules/projen/lib/ignore-file").IgnoreFile'. Types have separate declarations of a private property '_patterns'. ``` diff --git a/docs/content/getting_started/index.md b/docs/content/getting_started/index.md index 5e6d1251a..490315693 100644 --- a/docs/content/getting_started/index.md +++ b/docs/content/getting_started/index.md @@ -84,9 +84,9 @@ The AWS PDK lets you define your project structure as code in one of its support ```ts new AwsCdkTypeScriptApp({ - cdkVersion: "2.1.0", - name: "infra", - defaultReleaseBranch: "main", + cdkVersion: "2.1.0", + name: "infra", + defaultReleaseBranch: "main", }).synth(); ``` diff --git a/docs/content/getting_started/migration_guide.md b/docs/content/getting_started/migration_guide.md index caf90fb12..240b0f73b 100644 --- a/docs/content/getting_started/migration_guide.md +++ b/docs/content/getting_started/migration_guide.md @@ -28,11 +28,11 @@ We need to upgrade our dependencies to consume the new AWS PDK packages, to do s import { CloudscapeReactTsWebsiteProject } from "@aws-prototyping-sdk/cloudscape-react-ts-website"; import { NxMonorepoProject } from "@aws-prototyping-sdk/nx-monorepo"; import { - DocumentationFormat, - Language, - Library, - ModelLanguage, - TypeSafeApiProject, + DocumentationFormat, + Language, + Library, + ModelLanguage, + TypeSafeApiProject, } from "@aws-prototyping-sdk/type-safe-api"; import { javascript } from "projen"; import { AwsCdkTypeScriptApp } from "projen/lib/awscdk"; @@ -378,11 +378,255 @@ In order to make this construct truly cross-platform, we had to make a slight tw The `PDKPipelineTsProject` has been removed as it did not provide an adequate level of control of the generated pipeline. If you have instrumented this construct in your `projenrc.ts` file, you can simply change it to use `AwsCdkTypeScriptApp` instead and ensure the `sample` property is set to false to prevent additional sample code being generated. Be sure to ensure the construct has a dependency on `@aws/pdk` as you will still be able to use the `PDKPipeline` CDK construct contained within `@aws/pdk/pipeline`. -## TypeSafeApi breaking changes +## TypeSafeApi Breaking Changes + +### Request Parameters + +#### Types + +In the AWS Prototyping SDK, request parameters (query parameters, path parameters and header parameters) were provided to your handler methods or interceptors in two properties, "request parameters" and "request array parameters". These were typed as they were received by API Gateway without any marshalling, and as such no matter the types defined in your model, would be provided to your handlers as strings or string arrays. + +In AWS PDK, request parameters can all be found in the same "request parameters" property of the input, and are automatically marshalled into the type defined in your model. This means that any additional type coercion logic you may have written in your lambda handlers may need to be removed. + +For example, suppose we have an `Increment` operation defined in Smithy: + +```smithy +@readonly +@http(uri: "/increment", method: "GET") +operation Increment { + input := { + @required + @httpQuery("value") + value: Integer + } + output := { + @required + result: Integer + } +} +``` + +Previously, the `value` would be provided in `input.requestParameters.value` as a string, and your handler would have to handle converting it to a number (and returning an error response if it was not a number). Now, `value` is already converted to a number for you, and the handler wrapper will automatically respond with a `400` status code if the provided request parameter is not of the appropriate type. + +=== "BEFORE" + + ```ts + import { incrementHandler, Response } from "myapi-typescript-runtime"; + + export const handler = incrementHandler(async ({ input }) => { + const numberValue = Number(input.requestParameters.value); + if (isNaN(numberValue)) { + return Response.badRequest({ + message: 'value must be a number', + }); + } + return Response.success({ + result: numberValue + 1, + }); + }); + ``` + +=== "AFTER" + + ```ts + import { incrementHandler, Response } from "myapi-typescript-runtime"; + + export const handler = incrementHandler(async ({ input }) => { + return Response.success({ + result: input.requestParameters.value + 1, + }); + }); + ``` + +!!!warning + Request parameters have been restricted to only "primitives" (`number`, `integer`, `string` and `boolean`) or arrays of primitives, since this is the set of request parameters Smithy supports. Note also that a `string` of `date` or `date-time` format will be coerced into a language specific date object. + + While OpenAPI supports encoding objects as request parameters using various schemes, these are not supported by Type Safe API and you will receive a validation error if you specify `object` request parameters in your OpenAPI spec. + +#### TypeScript Interceptors + +You may have previously defined interceptors in TypeScript which referenced the `RequestArrayParameters` type in their type signature. Since all request parameters have been merged into a single `RequestParameters` type, `RequestArrayParameters` must be deleted from your interceptor type signatures: + +=== "BEFORE" + + ```ts hl_lines="9 15" + import { + sayHelloHandler, + ChainedRequestInput, + OperationResponse, + } from "myapi-typescript-runtime"; + + const tryCatchInterceptor = async < + RequestParameters, + RequestArrayParameters, + RequestBody, + Response extends OperationResponse + >( + request: ChainedRequestInput< + RequestParameters, + RequestArrayParameters, + RequestBody, + Response + > + ): Promise> => { + try { + return await request.chain.next(request); + } catch (e: any) { + return { statusCode: 500, body: { message: e.message } }; + } + }; -- TypeSafeApi Python runtime moved to new python-nextgen OpenAPI generator, which means: - - Properties of models can be referenced as attributes rather than dictionary syntax (eg .my_property rather than ["my_property"]). - - Models are now serialised and deserialised using the .from_json and .to_json methods. - - request_parameters and request_array_parameters are also models. -- TypeSafeApi Python handlers import location changed from .apis.tags.default_api_operation_config to .api.operation_config. -- TypeSafeApi Java classes for handlers/interceptors in runtime are no longer static subclasses of the Handlers class. The import location has changed from eg .api.Handlers.SayHello to .api.handlers.say_hello.SayHello. \ No newline at end of file +=== "AFTER" + + ```ts + import { + sayHelloHandler, + ChainedRequestInput, + OperationResponse, + } from "myapi-typescript-runtime"; + + const tryCatchInterceptor = async < + RequestParameters, + RequestBody, + Response extends OperationResponse + >( + request: ChainedRequestInput< + RequestParameters, + RequestBody, + Response + > + ): Promise> => { + try { + return await request.chain.next(request); + } catch (e: any) { + return { statusCode: 500, body: { message: e.message } }; + } + }; + +### Python Runtime Package + +The Python Runtime package has been migrated to use the latest python generator (`python-nextgen` in [OpenAPI Generator v6](https://github.com/OpenAPITools/openapi-generator/releases/tag/v6.6.0), and renamed to `python` in [v7](https://github.com/OpenAPITools/openapi-generator/releases/tag/v7.0.0)). + +As such, there are some breaking changes which will apply to any code using the models: + +#### Property Access + +Properties of models or request parameters previously needed to be accessed using dictionary notation, while they are now referenced through dot notation: + +=== "BEFORE" + + ```python hl_lines="9" + from myapi_python_runtime.model.say_hello_response_content import SayHelloResponseContent + from myapi_python_runtime.apis.tags.default_api_operation_config import say_hello_handler, + SayHelloRequest, SayHelloOperationResponses, ApiResponse + + @say_hello_handler + def handler(input: SayHelloRequest, **kwargs) -> SayHelloOperationResponses: + return ApiResponse( + status_code=200, + body=SayHelloResponseContent(message="Hello {}".format(input.request_parameters["name"])), + headers={} + ) + ``` + +=== "AFTER" + + ```python hl_lines="9" + from myapi_python_runtime.model.say_hello_response_content import SayHelloResponseContent + from myapi_python_runtime.api.operation_config import say_hello_handler, + SayHelloRequest, SayHelloOperationResponses, ApiResponse + from myapi_python_runtime.response import Response + + @say_hello_handler + def handler(input: SayHelloRequest, **kwargs) -> SayHelloOperationResponses: + return Response.success(SayHelloResponseContent( + message="Hello {}".format(input.request_parameters.name) + )) + ``` + +#### Import Path for Handler Wrappers + +The import path has changed for handler wrappers, so you will need to adjust your imports accordingly: + +=== "BEFORE" + + Previously handler wrappers were imported from `.apis.tags.default_api_operation_config`: + + ```python hl_lines="2-3" + from myapi_python_runtime.model.say_hello_response_content import SayHelloResponseContent + from myapi_python_runtime.apis.tags.default_api_operation_config import say_hello_handler, + SayHelloRequest, SayHelloOperationResponses, ApiResponse + + @say_hello_handler + def handler(input: SayHelloRequest, **kwargs) -> SayHelloOperationResponses: + return ApiResponse( + status_code=200, + body=SayHelloResponseContent(message="Hello {}".format(input.request_parameters["name"])), + headers={} + ) + ``` + +=== "AFTER" + + Handler wrappers are now imported from `.api.operation_config`: + + ```python hl_lines="2-3" + from myapi_python_runtime.model.say_hello_response_content import SayHelloResponseContent + from myapi_python_runtime.api.operation_config import say_hello_handler, + SayHelloRequest, SayHelloOperationResponses, ApiResponse + from myapi_python_runtime.response import Response + + @say_hello_handler + def handler(input: SayHelloRequest, **kwargs) -> SayHelloOperationResponses: + return Response.success(SayHelloResponseContent( + message="Hello {}".format(input.request_parameters.name) + )) + ``` + +### Java Runtime Package + +Previously in AWS Prototyping SDK, all classes pertaining to all operations were vended as static subclasses of the `Handlers` class. + +In AWS PDK, these classes have been moved out from `Handlers` as their own standalone classes. Additionally, classes are grouped under a package namespace for the particular operation. + +=== "BEFORE" + + ```java hl_lines="3-6" + package com.my.api; + + import com.generated.api.myapijavaruntime.runtime.api.Handlers.SayHello; + import com.generated.api.myapijavaruntime.runtime.api.Handlers.SayHello200Response; + import com.generated.api.myapijavaruntime.runtime.api.Handlers.SayHelloRequestInput; + import com.generated.api.myapijavaruntime.runtime.api.Handlers.SayHelloResponse; + import com.generated.api.myapijavaruntime.runtime.model.SayHelloResponseContent; + + public class SayHelloHandler extends SayHello { + @Override + public SayHelloResponse handle(SayHelloRequestInput request) { + return SayHello200Response.of(SayHelloResponseContent.builder() + .message(String.format("Hello %s", request.getInput().getRequestParameters().getName())) + .build()); + } + } + ``` + +=== "AFTER" + + ```java hl_lines="3-6" + package com.my.api; + + import com.generated.api.myapijavaruntime.runtime.api.handlers.say_hello.SayHello; + import com.generated.api.myapijavaruntime.runtime.api.handlers.say_hello.SayHello200Response; + import com.generated.api.myapijavaruntime.runtime.api.handlers.say_hello.SayHelloRequestInput; + import com.generated.api.myapijavaruntime.runtime.api.handlers.say_hello.SayHelloResponse; + import com.generated.api.myapijavaruntime.runtime.model.SayHelloResponseContent; + + public class SayHelloHandler extends SayHello { + @Override + public SayHelloResponse handle(final SayHelloRequestInput request) { + return SayHello200Response.of(SayHelloResponseContent.builder() + .message(String.format("Hello %s", request.getInput().getRequestParameters().getName())) + .build()); + } + } + ``` diff --git a/docs/content/getting_started/shopping_list_app.md b/docs/content/getting_started/shopping_list_app.md index 17cebbb95..582cbe330 100644 --- a/docs/content/getting_started/shopping_list_app.md +++ b/docs/content/getting_started/shopping_list_app.md @@ -30,6 +30,9 @@ This is a continuation from [build your first AWS PDK project](./your_first_aws_ In order to define our API, we need to write some [Smithy](https://smithy.io/2.0/quickstart.html) code which is contained within `packages/api/model/src/main/smithy`. +!!!info + More detailed information on how to use smithy can be found in the [Type Safe API Developer Guide](../developer_guides/type-safe-api/using_smithy.md) + ### Definining our types Firstly, let's define some types for our Shopping List application by creating the following files: diff --git a/docs/content/getting_started/your_first_aws_pdk_project.md b/docs/content/getting_started/your_first_aws_pdk_project.md index 67087cd70..c100fb5ca 100644 --- a/docs/content/getting_started/your_first_aws_pdk_project.md +++ b/docs/content/getting_started/your_first_aws_pdk_project.md @@ -541,16 +541,13 @@ Let's add this infrastructure to the monorepo by modifying our `projenrc` file t model: { language: ModelLanguage.SMITHY, options: { - smithy: { - serviceName: { - namespace: "com.aws", - serviceName: "MyApi", + smithy: { + serviceName: { + namespace: "com.aws", + serviceName: "MyApi", + }, }, }, - }, - }, - runtime: { - languages: [Language.TYPESCRIPT], }, documentation: { formats: [DocumentationFormat.HTML_REDOC], diff --git a/docs/content/overrides/home.html b/docs/content/overrides/home.html index b0f9cdfd2..67e373654 100644 --- a/docs/content/overrides/home.html +++ b/docs/content/overrides/home.html @@ -371,7 +371,7 @@
Getting started
- +