Skip to content

Commit

Permalink
Rust/cross service/rest aurora ses actix (awsdocs#3874)
Browse files Browse the repository at this point in the history
* Actix web healthz

* App create w/ deserialization

* Break out files in mod.

* Create item integration test.

* Comments

* Tracing!

* tmp unstash later

* Round trip to database!

* Finish repository & collection

* Add SES & send report

* Docs pass

* Top level comments

* Ignore environment specific files

* Refactored tests; mock tests

* Archived deserializer

* Clippy

* Formatted

* SPDX ID and comments review.

* Install libclang-dev for libxsmlwriter dependency

* sudo install me a dep

* Add placeholder test config

* Editorial

* Respond to Laren review.

Full archive test workflow.
Nits and fixes.
Spec diversions.
README!

* Add elwing item tracker readme

* Last comments

* Remove debugging line

* Laren editorial pass v2

* Editorial

Co-authored-by: Irene Smith <[email protected]>
  • Loading branch information
DavidSouther and irenepsmith authored Nov 10, 2022
1 parent d31d3a9 commit 4248c8b
Show file tree
Hide file tree
Showing 33 changed files with 1,980 additions and 3 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: pre-setup
run: sudo apt-get update && sudo apt-get install -y libclang-dev
- uses: actions-rs/toolchain@v1
with:
profile: minimal
Expand All @@ -36,3 +38,5 @@ jobs:
with:
command: test
args: --manifest-path rust_dev_preview/Cargo.toml
env:
APP_ENVIRONMENT: test
6 changes: 3 additions & 3 deletions .github/workflows/super-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ name: Lint Code Base

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

# Lets you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand All @@ -28,7 +28,7 @@ jobs:
- name: Lint Code Base
uses: github/super-linter@v4
env:
VALIDATE_ALL_CODEBASE: false
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_PHP_PHPCS: true
Expand Down
101 changes: 101 additions & 0 deletions resources/clients/react/elwing/src/plugins/item-tracker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Work item tracker Elwing Plugin

## Overview

Shows how to use [React](https://reactjs.org/) to create a web page that connects to a REST service that lets you do the following:

- Get a list of active or archived work items.
- Mark active work items as archived.
- Add new work items to the list of active items.
- Send a report of work items to a specified email recipient.

## Sample REST applications

The web client is designed to send requests to one of the following sample applications.
Each sample application shows you how to use an AWS SDK to store work items using AWS
resources:

- [Create a React and Spring REST application that queries Amazon Aurora Serverless data](/javav2/usecases/Creating_Spring_RDS_%20Rest)
- [Create the Amazon Aurora Serverless backend using the AWS SDK for PHP](/php/cross_service/aurora_item_tracker)
- [Track work items in an Aurora Serverless database with the SDK for Python](/python/cross_service/aurora_item_tracker)

## Run the Item Tracker UI

1. Run Elwing by following the instructions in the [Elwing README](/resources/clients/react/elwing/README.md).
1. When Elwing starts, a web browser opens http://localhost:3000/.
1. Run the item tracker plugin by selecting **Item Tracker** in the left navigation bar.
1. This sends a request to the REST service to get any existing active items:
```
GET http://localhost:8080/api/items?archived=false
```
1. At first, the table is empty.
![Work item tracker](images/item-tracker-start.png)
1. Select **Add item**, fill in the values, and select **Add** to add an item.
![Add item](images/item-tracker-add-item.png)
This sends a POST request to the REST service with a JSON payload that contains the
work item.
```
POST http://localhost:8080/api/items
{"name":"Me",
"guide":"Rust",
"description":"Show how to add an item",
"status":"In progress",
"archived":false}
```
1. After you add items, they display in the table.
You can archive an active item by selecting the **Archive** button next to the item.
![Work item tracker with items](images/item-tracker-all-items.png)
This sends a PUT request to the REST service, specifying the item ID and the `archive` action.
```
PUT http://localhost:8080/api/items/8db8aaa4-6f04-4467-bd60-EXAMPLEGUID:archive
```
1. To get and display items with a specific status, select a filter in the dropdown list, such as **Archived**.
![Work item tracker Archived items](images/item-tracker-archived-items.png)
This sends a GET request to the REST service with an `archived` query parameter.
```
GET http://localhost:8080/api/items?archived=true
```
1. Enter an email recipient and select **Send report** to send an email of active items.
![Work item tracker send report](images/item-tracker-send-report.png)
This sends a POST request to the REST service with a `report` action.
```
POST http://localhost:8080/api/items:report
```
When your Amazon Simple Email Service (Amazon SES) account is in the sandbox, both the sender and recipient email addresses must be registered with Amazon SES.
## Additional resources
<!--
- [.NET cross-service examples](/dotnetv3/cross-service/README.md)
- [Go cross-service examples](/gov2/cross_service)
- [JavaScript cross-service examples](/javascriptv3/example_code/cross-services)
- [Java cross-service examples](/javav2/usecases)
- [Kotlin cross-service examples](/kotlin/usecases/Readme.md)
- [Python cross-service examples](/python/cross_service/README.md)
-->
- [Rust cross-service examples](/rust_dev_preview/cross_service/rest_ses/README.md)
---
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions rust_dev_preview/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"config",
"cross_service/detect_faces",
"cross_service/detect_labels",
"cross_service/rest_ses",
"cross_service/telephone",
"dynamodb",
"ebs",
Expand Down
5 changes: 5 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.
!configuration
!src
!tests
!Cargo.toml
47 changes: 47 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "rest_ses"
version = "0.1.0"
edition = "2021"


[lib]
path = "src/lib.rs"

[[bin]]
path = "src/main.rs"
name = "rest_ses"

[dependencies]
actix-web = "4"
actix-web-prom = "0.6.0"
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-cloudwatchlogs = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-rdsdata = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
aws-sdk-ses = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
chrono = { version = "0.4.22", default-features = false, features = ["clock", "serde"] }
color-eyre = "0.6.2"
config = "0.13.2"
derive_more = "0.99.17"
futures = "0.3.24"
mail-builder = "0.2.2"
reqwest = "0.11.12"
secrecy = { version = "0.8.0", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.86"
thiserror = "1.0.37"
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
tracing = "0.1.37"
tracing-actix-web = "0.6.1"
tracing-bunyan-formatter = "0.3.4"
tracing-log = "0.1.3"
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
uuid = { version = "1.2.1", features = ["v4", "serde"] }
xlsxwriter = "0.3.1"

[dev-dependencies]
aws-smithy-http = { git = "https://github.com/awslabs/aws-sdk-rust", branch = "main" }
fake = { version = "2.5.0", features = ["uuid"] }
once_cell = "1.15.0"
rand = "0.8.5"
tokio-test = "0.4.2"
wiremock = "0.5.15"
32 changes: 32 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# chef and planner steps pre-compile the upstream create dependencies.
FROM lukemathwalker/cargo-chef:latest-rust-1.59.0 as chef
WORKDIR /app
RUN apt update && apt install lld clang -y

FROM chef as planner
COPY Cargo.toml .
# Compute a lock-like file for our project.
RUN cargo chef prepare --recipe-path recipe.json

# After pre-building the crates, build the app binary.
FROM chef as builder
COPY --from=planner /app/recipe.json recipe.json
# Build our project dependencies, not our application.
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
# Build our project.
RUN cargo build --release --bin rest_ses

# Take the final binary from the builder, and the local configuration files.
FROM debian:bullseye-slim AS runtime
WORKDIR /app
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends openssl ca-certificates \
# Clean up.
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/rest_ses rest_ses
COPY configuration configuration
ENV APP_ENVIRONMENT production
ENTRYPOINT ["./rest_ses"]
155 changes: 155 additions & 0 deletions rust_dev_preview/cross_service/rest_ses/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Track work items in an Aurora Serverless database with the SDK for Rust

## Overview

This example shows you how to use the AWS SDK for Rust to create a REST service that lets you do the following:

- Build an Actix Web REST service that integrates with AWS services.
- Read, write, and update work items that are stored in an Amazon Aurora Serverless database.
- Create an AWS Secrets Manager secret that contains database credentials and use it to authenticate calls to the database.
- Use Amazon Simple Email Service (Amazon SES) to send email reports of work items.

The REST service is used in conjunction with the [Elwing React client](../../../resources/clients/react/elwing)
to present a fully functional web application.

### ⚠️ Important

- Running this code might result in charges to your AWS account.
- Running the tests might result in charges to your AWS account.
- We recommend that you grant your code least privilege. At most, grant only the minimum
permissions required to perform the task. For more information, see
[Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
- This code is not tested in every AWS Region. For more information, see
[AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).

### Prerequisites

Prerequisites for running examples can be found in the [README](../../README.md#Prerequisites) in the Rust folder.

## Create the resources

Follow the instructions in the [README for the Aurora Serverless application](/resources/cdk/aurora_serverless_app/README.md) to use the AWS Cloud Development Kit (AWS CDK) or AWS Command Line Interface (AWS CLI) to create and manage the resources.

## Run the example

### REST service

#### Configure the service

Before you run the service, enter your AWS resource values and verified email address
in `configuration/local.yaml`, similar to the following:

```yaml
rds:
db_instance: auroraappdb
secret_arn: "arn:aws:secretsmanager:us-east-1:1111222233334444:secret:docexampleauroraappsecret8B-EXAMPLE-Dz2N2y"
cluster_arn: "arn:aws:rds:us-east-1:1111222233334444:cluster:docexampleauroraapp-docexampleauroraappclustereb7e-EXAMPLE"
ses:
source: "[email protected]" # Replace with an email address that is registered with Amazon SES.
```
#### Run the service
This example uses [Actix Web](https://actix.rs/) to host a local web server and REST service.
With the web server running, you can send HTTP requests to the service endpoint to list, add, and update work items and to send email reports.
```
cargo run
```

### Webpage

The REST service is designed to work with the item tracker plugin in the Elwing web client.
The item tracker plugin is a JavaScript application that lets you manage work items, send requests to the REST service, and see the results.

#### Run Elwing and select the item tracker

1. Run Elwing by following the instructions in the [Elwing README](/resources/clients/react/elwing/README.md).
1. When Elwing starts, a web browser opens http://localhost:3000/.
1. Run the item tracker plugin by selecting **Item Tracker** in the left navigation bar.
1. Follow the Elwing [Item Tracker instructions](/resources/clients/react/elwing/src/plugins/item-tracker/README.md).

## Understand the example

This example uses the Actix Web framework to host a local REST service and respond to HTTP requests.

### Start up

When the program first starts, it loads environment and configuration information from `src/configuration.rs`.
It then prepares its Actix Web resources and SDK clients in `src/startup.rs`.
These helpers are also used in `test/startup.rs` to ensure the application is running in a consistent environment in development, testing, and production.

### Routing

Top level routing happens when creating the HTTP server in `src/startup.rs`, with specific routes registered in `src/healthz.rs`, `src/collection.rs`, and `src/report.rs`.
All routes are instrumented, and primarily serve as a facade between Actix's HTTP tooling and the SDK resources.

```Rust
/// Retrieve a single WorkItem, in a JSON body, specified by a URL parameter.
#[actix_web::get("/{id}")]
#[tracing::instrument(name = "Request Retrieve single WorkItem", skip(client))]
async fn retrieve(
itemid: Path<String>,
client: Data<crate::client::RdsClient>,
) -> Result<Json<crate::work_item::WorkItem>, crate::work_item::WorkItemError> {
crate::work_item::repository::retrieve(itemid.to_string(), &client)
.await
.map(Json)
}
```

### Aurora Serverless repository

The [repository.rs](src/work_item/repository.rs) file contains functions that get and set data in an Aurora Serverless database by using an Amazon Relational Database Service (Amazon RDS) Data Service client.

For example, the `retrieve` function constructs a `SELECT` statement and parameters and sends them to the data client to get a single work item.

```Rust
pub async fn retrieve(id: String, client: &crate::client::RdsClient) -> Result<crate::work_item::WorkItem, crate::work_item::WorkItemError> {
let statement = client
.execute_statement()
.sql(format!(
r#"SELECT {FIELDS} FROM Work WHERE idwork = :idwork;"#
))
.set_parameters(params![("idwork", id)])
.format_records_as(RecordsFormatType::Json)
.send()
.await;

let items = parse_rds_output(statement)?;
todo!("Further checks to ensure a single item was retrieved")
}
```

### Amazon SES report

The [report.rs](src/report.rs) file contains functions that route the report HTTP request and send an email report of work items to a specified email address.

The report is send as an XSLX Excel spreadsheet.
When you use Amazon SES to send an attachment, you must use the `send_raw_email` service action and send the email in MIME format.

## Delete the resources

To avoid charges, delete all the resources that you created for this tutorial.

If you created the example resources by using the AWS CDK or AWS CLI, you can destroy the resources by following the instructions in the [README for the Aurora Serverless application](/resources/cdk/aurora_serverless_app/README.md).

If you created your resources through the AWS Management Console, or modified them by running the app, you must use the console to delete them.

## Next steps

Congratulations!
You have built a REST service that reads, writes, and archives work items that are stored in an Aurora Serverless database, and that uses Amazon SES to send email to a registered user.

## Additional information

- [Amazon Aurora User Guide](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html)
- [Amazon RDS User Guide](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html)
- [Amazon SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/Welcome.html)
- [Amazon RDS Data Service Rust SDK API Reference](https://crates.io/crates/aws-sdk-rdsdata)

---

Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
local.yaml
production.yaml
Loading

0 comments on commit 4248c8b

Please sign in to comment.