From bfbbf96c41fecab95424226db2a551cd03b703cf Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Sat, 5 Dec 2020 17:07:31 +0000 Subject: [PATCH 01/10] Chapter 05 (snapshot) --- chapter05/.dockerignore => .dockerignore | 0 chapter03-1/.env => .env | 0 .github/workflows/general.yml | 9 +- Cargo.lock | 93 +++--------- Cargo.toml | 36 ++++- chapter05/Dockerfile => Dockerfile | 4 +- chapter03-0/Cargo.toml | 21 --- chapter03-0/src/lib.rs | 14 -- chapter03-0/src/main.rs | 8 - chapter03-0/tests/health_check.rs | 31 ---- chapter03-1/Cargo.toml | 26 ---- chapter03-1/configuration.yaml | 7 - chapter03-1/src/configuration.rs | 38 ----- chapter03-1/src/lib.rs | 4 - chapter03-1/src/main.rs | 23 --- chapter03-1/src/routes/subscriptions.rs | 33 ---- chapter03-1/src/startup.rs | 19 --- chapter03-1/tests/health_check.rs | 129 ---------------- chapter04/.env | 1 - chapter04/Cargo.toml | 33 ---- chapter04/configuration.yaml | 7 - ...00823135036_create_subscriptions_table.sql | 8 - chapter04/scripts/init_db.sh | 40 ----- chapter04/src/configuration.rs | 38 ----- chapter04/src/main.rs | 27 ---- chapter04/src/routes/health_check.rs | 5 - chapter04/src/routes/mod.rs | 5 - chapter04/tests/health_check.rs | 143 ------------------ chapter05/.env | 1 - chapter05/Cargo.toml | 34 ----- ...00823135036_create_subscriptions_table.sql | 8 - chapter05/scripts/init_db.sh | 40 ----- chapter05/src/lib.rs | 5 - chapter05/src/routes/health_check.rs | 5 - chapter05/src/routes/mod.rs | 5 - chapter05/src/routes/subscriptions.rs | 52 ------- chapter05/src/startup.rs | 21 --- chapter05/src/telemetry.rs | 29 ---- .../configuration => configuration}/base.yaml | 0 .../local.yaml | 0 .../production.yaml | 0 ...00823135036_create_subscriptions_table.sql | 0 {chapter03-1/scripts => scripts}/init_db.sh | 0 chapter05/spec.yaml => spec.yaml | 4 +- chapter05/sqlx-data.json => sqlx-data.json | 0 {chapter05/src => src}/configuration.rs | 0 {chapter04/src => src}/lib.rs | 0 {chapter05/src => src}/main.rs | 6 +- .../src => src}/routes/health_check.rs | 0 {chapter03-1/src => src}/routes/mod.rs | 0 .../src => src}/routes/subscriptions.rs | 0 {chapter04/src => src}/startup.rs | 0 {chapter04/src => src}/telemetry.rs | 0 {chapter05/tests => tests}/health_check.rs | 6 +- 54 files changed, 75 insertions(+), 943 deletions(-) rename chapter05/.dockerignore => .dockerignore (100%) rename chapter03-1/.env => .env (100%) rename chapter05/Dockerfile => Dockerfile (91%) delete mode 100644 chapter03-0/Cargo.toml delete mode 100644 chapter03-0/src/lib.rs delete mode 100644 chapter03-0/src/main.rs delete mode 100644 chapter03-0/tests/health_check.rs delete mode 100644 chapter03-1/Cargo.toml delete mode 100644 chapter03-1/configuration.yaml delete mode 100644 chapter03-1/src/configuration.rs delete mode 100644 chapter03-1/src/lib.rs delete mode 100644 chapter03-1/src/main.rs delete mode 100644 chapter03-1/src/routes/subscriptions.rs delete mode 100644 chapter03-1/src/startup.rs delete mode 100644 chapter03-1/tests/health_check.rs delete mode 100644 chapter04/.env delete mode 100644 chapter04/Cargo.toml delete mode 100644 chapter04/configuration.yaml delete mode 100644 chapter04/migrations/20200823135036_create_subscriptions_table.sql delete mode 100755 chapter04/scripts/init_db.sh delete mode 100644 chapter04/src/configuration.rs delete mode 100644 chapter04/src/main.rs delete mode 100644 chapter04/src/routes/health_check.rs delete mode 100644 chapter04/src/routes/mod.rs delete mode 100644 chapter04/tests/health_check.rs delete mode 100644 chapter05/.env delete mode 100644 chapter05/Cargo.toml delete mode 100644 chapter05/migrations/20200823135036_create_subscriptions_table.sql delete mode 100755 chapter05/scripts/init_db.sh delete mode 100644 chapter05/src/lib.rs delete mode 100644 chapter05/src/routes/health_check.rs delete mode 100644 chapter05/src/routes/mod.rs delete mode 100644 chapter05/src/routes/subscriptions.rs delete mode 100644 chapter05/src/startup.rs delete mode 100644 chapter05/src/telemetry.rs rename {chapter05/configuration => configuration}/base.yaml (100%) rename {chapter05/configuration => configuration}/local.yaml (100%) rename {chapter05/configuration => configuration}/production.yaml (100%) rename {chapter03-1/migrations => migrations}/20200823135036_create_subscriptions_table.sql (100%) rename {chapter03-1/scripts => scripts}/init_db.sh (100%) rename chapter05/spec.yaml => spec.yaml (95%) rename chapter05/sqlx-data.json => sqlx-data.json (100%) rename {chapter05/src => src}/configuration.rs (100%) rename {chapter04/src => src}/lib.rs (100%) rename {chapter05/src => src}/main.rs (84%) rename {chapter03-1/src => src}/routes/health_check.rs (100%) rename {chapter03-1/src => src}/routes/mod.rs (100%) rename {chapter04/src => src}/routes/subscriptions.rs (100%) rename {chapter04/src => src}/startup.rs (100%) rename {chapter04/src => src}/telemetry.rs (100%) rename {chapter05/tests => tests}/health_check.rs (96%) diff --git a/chapter05/.dockerignore b/.dockerignore similarity index 100% rename from chapter05/.dockerignore rename to .dockerignore diff --git a/chapter03-1/.env b/.env similarity index 100% rename from chapter03-1/.env rename to .env diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index e417859..f7207a7 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -1,6 +1,13 @@ name: Rust -on: [push, pull_request] +on: + # NB: this differs from the book's project! + # These settings allow us to run this specific CI pipeline for PRs against + # this specific branch (a.k.a. book chapter). + pull_request: + types: [ opened, synchronize, reopened ] + branches: + - root-chapter-05 env: CARGO_TERM_COLOR: always diff --git a/Cargo.lock b/Cargo.lock index 9bf71a9..7b76292 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,76 +473,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chapter03-0" -version = "0.1.0" -dependencies = [ - "actix-rt", - "actix-web", - "reqwest", - "tokio", -] - -[[package]] -name = "chapter03-1" -version = "0.1.0" -dependencies = [ - "actix-rt", - "actix-web", - "chrono", - "config", - "reqwest", - "serde", - "sqlx", - "tokio", - "uuid", -] - -[[package]] -name = "chapter04" -version = "0.1.0" -dependencies = [ - "actix-rt", - "actix-web", - "chrono", - "config", - "lazy_static", - "reqwest", - "serde", - "sqlx", - "tokio", - "tracing", - "tracing-actix-web", - "tracing-bunyan-formatter", - "tracing-futures", - "tracing-log", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "chapter05" -version = "0.1.0" -dependencies = [ - "actix-rt", - "actix-web", - "chrono", - "config", - "lazy_static", - "reqwest", - "serde", - "serde-aux", - "sqlx", - "tokio", - "tracing", - "tracing-actix-web", - "tracing-bunyan-formatter", - "tracing-futures", - "tracing-log", - "tracing-subscriber", - "uuid", -] - [[package]] name = "chrono" version = "0.4.19" @@ -2730,3 +2660,26 @@ checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zero2prod" +version = "0.1.0" +dependencies = [ + "actix-rt", + "actix-web", + "chrono", + "config", + "lazy_static", + "reqwest", + "serde", + "serde-aux", + "sqlx", + "tokio", + "tracing", + "tracing-actix-web", + "tracing-bunyan-formatter", + "tracing-futures", + "tracing-log", + "tracing-subscriber", + "uuid", +] diff --git a/Cargo.toml b/Cargo.toml index 7b05a59..278d87c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,34 @@ -[workspace] -members = ["chapter03-0", "chapter03-1", "chapter04", "chapter05"] +[package] +name = "zero2prod" +version = "0.1.0" +authors = ["LukeMathWalker "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +path = "src/lib.rs" + +[[bin]] +path = "src/main.rs" +name = "zero2prod" + +[dependencies] +actix-web = "3.0.0" +actix-rt = "1.1.1" +tokio = "0.2.22" +serde = "1.0.115" +config = { version = "0.10.1", default-features = false, features = ["yaml"] } +sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] } +uuid = { version = "0.8.1", features = ["v4"] } +chrono = "0.4.15" +tracing = "0.1.19" +tracing-futures = "0.2.4" +tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } +tracing-bunyan-formatter = "0.1.6" +tracing-log = "0.1.1" +tracing-actix-web = "0.2.0" +serde-aux = "1.0.1" + +[dev-dependencies] +reqwest = { version = "0.10.7", features = ["json"] } +lazy_static = "1.4.0" diff --git a/chapter05/Dockerfile b/Dockerfile similarity index 91% rename from chapter05/Dockerfile rename to Dockerfile index 9b72852..e90c7c6 100644 --- a/chapter05/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ COPY --from=cacher /usr/local/cargo /usr/local/cargo COPY . . # Build our application, leveraging the cached deps! ENV SQLX_OFFLINE true -RUN cargo build --release --bin chapter05 +RUN cargo build --release --bin zero2prod FROM debian:buster-slim AS runtime WORKDIR app @@ -32,7 +32,7 @@ RUN apt-get update -y \ && apt-get install -y --no-install-recommends openssl \ # Clean up && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* -COPY --from=builder /app/target/release/chapter05 zero2prod +COPY --from=builder /app/target/release/zero2prod zero2prod COPY configuration configuration ENV APP_ENVIRONMENT production ENTRYPOINT ["./zero2prod"] diff --git a/chapter03-0/Cargo.toml b/chapter03-0/Cargo.toml deleted file mode 100644 index 4e5dab3..0000000 --- a/chapter03-0/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "chapter03-0" -version = "0.1.0" -authors = ["LukeMathWalker "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -path = "src/lib.rs" - -[[bin]] -path = "src/main.rs" -name = "chapter03-0" - -[dependencies] -actix-web = "3.0.0" -actix-rt = "1.1.1" -tokio = "0.2.22" - -[dev-dependencies] -reqwest = "0.10.7" diff --git a/chapter03-0/src/lib.rs b/chapter03-0/src/lib.rs deleted file mode 100644 index d4321d5..0000000 --- a/chapter03-0/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -use actix_web::dev::Server; -use actix_web::{web, App, HttpResponse, HttpServer}; -use std::net::TcpListener; - -async fn health_check() -> HttpResponse { - HttpResponse::Ok().finish() -} - -pub fn run(listener: TcpListener) -> Result { - let server = HttpServer::new(|| App::new().route("/health_check", web::get().to(health_check))) - .listen(listener)? - .run(); - Ok(server) -} diff --git a/chapter03-0/src/main.rs b/chapter03-0/src/main.rs deleted file mode 100644 index cb6021e..0000000 --- a/chapter03-0/src/main.rs +++ /dev/null @@ -1,8 +0,0 @@ -use chapter03_0::run; -use std::net::TcpListener; - -#[actix_rt::main] -async fn main() -> std::io::Result<()> { - let address = TcpListener::bind("127.0.0.1:8000")?; - run(address)?.await -} diff --git a/chapter03-0/tests/health_check.rs b/chapter03-0/tests/health_check.rs deleted file mode 100644 index 98596a1..0000000 --- a/chapter03-0/tests/health_check.rs +++ /dev/null @@ -1,31 +0,0 @@ -use chapter03_0::run; -use std::net::TcpListener; - -fn spawn_app() -> String { - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); - // We retrieve the port assigned to us by the OS - let port = listener.local_addr().unwrap().port(); - let server = run(listener).expect("Failed to bind address"); - let _ = tokio::spawn(server); - // We return the application address to the caller! - format!("http://127.0.0.1:{}", port) -} - -#[actix_rt::test] -async fn health_check_works() { - // Arrange - let address = spawn_app(); - let client = reqwest::Client::new(); - - // Act - let response = client - // Use the returned application address - .get(&format!("{}/health_check", &address)) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert!(response.status().is_success()); - assert_eq!(Some(0), response.content_length()); -} diff --git a/chapter03-1/Cargo.toml b/chapter03-1/Cargo.toml deleted file mode 100644 index 2164985..0000000 --- a/chapter03-1/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "chapter03-1" -version = "0.1.0" -authors = ["LukeMathWalker "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -path = "src/lib.rs" - -[[bin]] -path = "src/main.rs" -name = "chapter03-1" - -[dependencies] -actix-web = "3.0.0" -actix-rt = "1.1.1" -tokio = "0.2.22" -serde = "1.0.115" -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "postgres", "uuid", "chrono", "migrate"] } -uuid = { version = "0.8.1", features = ["v4"] } -chrono = "0.4.15" - -[dev-dependencies] -reqwest = { version = "0.10.7", features = ["json"] } diff --git a/chapter03-1/configuration.yaml b/chapter03-1/configuration.yaml deleted file mode 100644 index 04dc5ef..0000000 --- a/chapter03-1/configuration.yaml +++ /dev/null @@ -1,7 +0,0 @@ -application_port: 8000 -database: - host: "localhost" - port: 5432 - username: "postgres" - password: "password" - database_name: "newsletter" \ No newline at end of file diff --git a/chapter03-1/src/configuration.rs b/chapter03-1/src/configuration.rs deleted file mode 100644 index 3ad6da7..0000000 --- a/chapter03-1/src/configuration.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[derive(serde::Deserialize)] -pub struct Settings { - pub database: DatabaseSettings, - pub application_port: u16, -} - -#[derive(serde::Deserialize)] -pub struct DatabaseSettings { - pub username: String, - pub password: String, - pub port: u16, - pub host: String, - pub database_name: String, -} - -impl DatabaseSettings { - pub fn connection_string(&self) -> String { - format!( - "postgres://{}:{}@{}:{}/{}", - self.username, self.password, self.host, self.port, self.database_name - ) - } - - pub fn connection_string_without_db(&self) -> String { - format!( - "postgres://{}:{}@{}:{}", - self.username, self.password, self.host, self.port - ) - } -} - -pub fn get_configuration() -> Result { - let mut settings = config::Config::default(); - - settings.merge(config::File::with_name("configuration"))?; - - settings.try_into() -} diff --git a/chapter03-1/src/lib.rs b/chapter03-1/src/lib.rs deleted file mode 100644 index b477f22..0000000 --- a/chapter03-1/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(clippy::toplevel_ref_arg)] -pub mod configuration; -pub mod routes; -pub mod startup; diff --git a/chapter03-1/src/main.rs b/chapter03-1/src/main.rs deleted file mode 100644 index b4f54f4..0000000 --- a/chapter03-1/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -use chapter03_1::configuration::get_configuration; -use chapter03_1::startup::run; -use sqlx::postgres::PgPool; -use std::net::TcpListener; - -#[actix_rt::main] -async fn main() -> std::io::Result<()> { - let configuration = get_configuration().expect("Failed to read configuration."); - let connection_pool = PgPool::connect(&configuration.database.connection_string()) - .await - .expect("Failed to connect to Postgres."); - - // Here we choose to bind explicitly to localhost, 127.0.0.1, for security - // reasons. This binding may cause issues in some environments. For example, - // it causes connectivity issues running in WSL2, where you cannot reach the - // server when it is bound to WSL2's localhost interface. As a workaround, - // you can choose to bind to all interfaces, 0.0.0.0, instead, but be aware - // of the security implications when you expose the server on all interfaces. - let address = format!("127.0.0.1:{}", configuration.application_port); - let listener = TcpListener::bind(address)?; - run(listener, connection_pool)?.await?; - Ok(()) -} diff --git a/chapter03-1/src/routes/subscriptions.rs b/chapter03-1/src/routes/subscriptions.rs deleted file mode 100644 index 8db4b58..0000000 --- a/chapter03-1/src/routes/subscriptions.rs +++ /dev/null @@ -1,33 +0,0 @@ -use actix_web::{web, HttpResponse}; -use chrono::Utc; -use sqlx::PgPool; -use uuid::Uuid; - -#[derive(serde::Deserialize)] -pub struct FormData { - email: String, - name: String, -} - -pub async fn subscribe( - form: web::Form, - pool: web::Data, -) -> Result { - sqlx::query!( - r#" - INSERT INTO subscriptions (id, email, name, subscribed_at) - VALUES ($1, $2, $3, $4) - "#, - Uuid::new_v4(), - form.email, - form.name, - Utc::now() - ) - .execute(pool.as_ref()) - .await - .map_err(|e| { - println!("Failed to execute query: {}", e); - HttpResponse::InternalServerError().finish() - })?; - Ok(HttpResponse::Ok().finish()) -} diff --git a/chapter03-1/src/startup.rs b/chapter03-1/src/startup.rs deleted file mode 100644 index 9f35c15..0000000 --- a/chapter03-1/src/startup.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::routes::{health_check, subscribe}; -use actix_web::dev::Server; -use actix_web::web::Data; -use actix_web::{web, App, HttpServer}; -use sqlx::PgPool; -use std::net::TcpListener; - -pub fn run(listener: TcpListener, db_pool: PgPool) -> Result { - let db_pool = Data::new(db_pool); - let server = HttpServer::new(move || { - App::new() - .route("/health_check", web::get().to(health_check)) - .route("/subscriptions", web::post().to(subscribe)) - .app_data(db_pool.clone()) - }) - .listen(listener)? - .run(); - Ok(server) -} diff --git a/chapter03-1/tests/health_check.rs b/chapter03-1/tests/health_check.rs deleted file mode 100644 index 61de9c6..0000000 --- a/chapter03-1/tests/health_check.rs +++ /dev/null @@ -1,129 +0,0 @@ -use chapter03_1::configuration::{get_configuration, DatabaseSettings}; -use chapter03_1::startup::run; -use sqlx::{Connection, Executor, PgConnection, PgPool}; -use std::net::TcpListener; -use uuid::Uuid; - -pub struct TestApp { - pub address: String, - pub db_pool: PgPool, -} - -async fn spawn_app() -> TestApp { - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); - // We retrieve the port assigned to us by the OS - let port = listener.local_addr().unwrap().port(); - let address = format!("http://127.0.0.1:{}", port); - - let mut configuration = get_configuration().expect("Failed to read configuration."); - configuration.database.database_name = Uuid::new_v4().to_string(); - let connection_pool = configure_database(&configuration.database).await; - - let server = run(listener, connection_pool.clone()).expect("Failed to bind address"); - let _ = tokio::spawn(server); - TestApp { - address, - db_pool: connection_pool, - } -} - -pub async fn configure_database(config: &DatabaseSettings) -> PgPool { - // Create database - let mut connection = PgConnection::connect(&config.connection_string_without_db()) - .await - .expect("Failed to connect to Postgres"); - connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) - .await - .expect("Failed to create database."); - - // Migrate database - let connection_pool = PgPool::connect(&config.connection_string()) - .await - .expect("Failed to connect to Postgres."); - sqlx::migrate!("./migrations") - .run(&connection_pool) - .await - .expect("Failed to migrate the database"); - - connection_pool -} - -#[actix_rt::test] -async fn health_check_works() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - - // Act - let response = client - // Use the returned application address - .get(&format!("{}/health_check", &app.address)) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert!(response.status().is_success()); - assert_eq!(Some(0), response.content_length()); -} - -#[actix_rt::test] -async fn subscribe_returns_a_200_for_valid_form_data() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; - - // Act - let response = client - .post(&format!("{}/subscriptions", &app.address)) - .header("Content-Type", "application/x-www-form-urlencoded") - .body(body) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert_eq!(200, response.status().as_u16()); - - let saved = sqlx::query!("SELECT email, name FROM subscriptions",) - .fetch_one(&app.db_pool) - .await - .expect("Failed to fetch saved subscription."); - - assert_eq!(saved.email, "ursula_le_guin@gmail.com"); - assert_eq!(saved.name, "le guin"); -} - -#[actix_rt::test] -async fn subscribe_returns_a_400_when_data_is_missing() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - let test_cases = vec![ - ("name=le%20guin", "missing the email"), - ("email=ursula_le_guin%40gmail.com", "missing the name"), - ("", "missing both name and email"), - ]; - - for (invalid_body, error_message) in test_cases { - // Act - let response = client - .post(&format!("{}/subscriptions", &app.address)) - .header("Content-Type", "application/x-www-form-urlencoded") - .body(invalid_body) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert_eq!( - 400, - response.status().as_u16(), - // Additional customised error message on test failure - "The API did not fail with 400 Bad Request when the payload was {}.", - error_message - ); - } -} diff --git a/chapter04/.env b/chapter04/.env deleted file mode 100644 index 88cfb53..0000000 --- a/chapter04/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgres://postgres:password@localhost:5432/newsletter" diff --git a/chapter04/Cargo.toml b/chapter04/Cargo.toml deleted file mode 100644 index 01edb36..0000000 --- a/chapter04/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "chapter04" -version = "0.1.0" -authors = ["LukeMathWalker "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -path = "src/lib.rs" - -[[bin]] -path = "src/main.rs" -name = "chapter04" - -[dependencies] -actix-web = "3.0.0" -actix-rt = "1.1.1" -tokio = "0.2.22" -serde = "1.0.115" -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "postgres", "uuid", "chrono", "migrate"] } -uuid = { version = "0.8.1", features = ["v4"] } -chrono = "0.4.15" -tracing = "0.1.19" -tracing-futures = "0.2.4" -tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } -tracing-bunyan-formatter = "0.1.6" -tracing-log = "0.1.1" -tracing-actix-web = "0.2.0" - -[dev-dependencies] -reqwest = { version = "0.10.7", features = ["json"] } -lazy_static = "1.4.0" diff --git a/chapter04/configuration.yaml b/chapter04/configuration.yaml deleted file mode 100644 index 04dc5ef..0000000 --- a/chapter04/configuration.yaml +++ /dev/null @@ -1,7 +0,0 @@ -application_port: 8000 -database: - host: "localhost" - port: 5432 - username: "postgres" - password: "password" - database_name: "newsletter" \ No newline at end of file diff --git a/chapter04/migrations/20200823135036_create_subscriptions_table.sql b/chapter04/migrations/20200823135036_create_subscriptions_table.sql deleted file mode 100644 index 2c0d262..0000000 --- a/chapter04/migrations/20200823135036_create_subscriptions_table.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Create Subscriptions Table -CREATE TABLE subscriptions( - id uuid NOT NULL, - PRIMARY KEY (id), - email TEXT NOT NULL UNIQUE, - name TEXT NOT NULL, - subscribed_at timestamptz NOT NULL -); \ No newline at end of file diff --git a/chapter04/scripts/init_db.sh b/chapter04/scripts/init_db.sh deleted file mode 100755 index cc1df04..0000000 --- a/chapter04/scripts/init_db.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -set -x -set -eo pipefail - -# Check if a custom user has been set, otherwise default to 'postgres' -DB_USER=${POSTGRES_USER:=postgres} -# Check if a custom password has been set, otherwise default to 'password' -DB_PASSWORD="${POSTGRES_PASSWORD:=password}" -# Check if a custom password has been set, otherwise default to 'newsletter' -DB_NAME="${POSTGRES_DB:=newsletter}" -# Check if a custom port has been set, otherwise default to '5432' -DB_PORT="${POSTGRES_PORT:=5432}" - -# Allow to skip Docker if a dockerized Postgres database is already running -if [[ -z "${SKIP_DOCKER}" ]] -then - # Launch postgres using Docker - docker run \ - -e POSTGRES_USER=${DB_USER} \ - -e POSTGRES_PASSWORD=${DB_PASSWORD} \ - -e POSTGRES_DB=${DB_NAME} \ - -p "${DB_PORT}":5432 \ - -d postgres \ - postgres -N 1000 - # ^ Increased maximum number of connections for testing purposes -fi - -# Keep pinging Postgres until it's ready to accept commands -until PGPASSWORD="${DB_PASSWORD}" psql -h "localhost" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do - >&2 echo "Postgres is still unavailable - sleeping" - sleep 1 -done - ->&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!" - -export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME} -sqlx database create -sqlx migrate run - ->&2 echo "Postgres has been migrated, ready to go!" diff --git a/chapter04/src/configuration.rs b/chapter04/src/configuration.rs deleted file mode 100644 index 3ad6da7..0000000 --- a/chapter04/src/configuration.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[derive(serde::Deserialize)] -pub struct Settings { - pub database: DatabaseSettings, - pub application_port: u16, -} - -#[derive(serde::Deserialize)] -pub struct DatabaseSettings { - pub username: String, - pub password: String, - pub port: u16, - pub host: String, - pub database_name: String, -} - -impl DatabaseSettings { - pub fn connection_string(&self) -> String { - format!( - "postgres://{}:{}@{}:{}/{}", - self.username, self.password, self.host, self.port, self.database_name - ) - } - - pub fn connection_string_without_db(&self) -> String { - format!( - "postgres://{}:{}@{}:{}", - self.username, self.password, self.host, self.port - ) - } -} - -pub fn get_configuration() -> Result { - let mut settings = config::Config::default(); - - settings.merge(config::File::with_name("configuration"))?; - - settings.try_into() -} diff --git a/chapter04/src/main.rs b/chapter04/src/main.rs deleted file mode 100644 index 6d032d2..0000000 --- a/chapter04/src/main.rs +++ /dev/null @@ -1,27 +0,0 @@ -use chapter04::configuration::get_configuration; -use chapter04::startup::run; -use chapter04::telemetry::{get_subscriber, init_subscriber}; -use sqlx::postgres::PgPool; -use std::net::TcpListener; - -#[actix_rt::main] -async fn main() -> std::io::Result<()> { - let subscriber = get_subscriber("zero2prod".into(), "info".into()); - init_subscriber(subscriber); - - let configuration = get_configuration().expect("Failed to read configuration."); - let connection_pool = PgPool::connect(&configuration.database.connection_string()) - .await - .expect("Failed to connect to Postgres."); - - // Here we choose to bind explicitly to localhost, 127.0.0.1, for security - // reasons. This binding may cause issues in some environments. For example, - // it causes connectivity issues running in WSL2, where you cannot reach the - // server when it is bound to WSL2's localhost interface. As a workaround, - // you can choose to bind to all interfaces, 0.0.0.0, instead, but be aware - // of the security implications when you expose the server on all interfaces. - let address = format!("127.0.0.1:{}", configuration.application_port); - let listener = TcpListener::bind(address)?; - run(listener, connection_pool)?.await?; - Ok(()) -} diff --git a/chapter04/src/routes/health_check.rs b/chapter04/src/routes/health_check.rs deleted file mode 100644 index d7eb4e0..0000000 --- a/chapter04/src/routes/health_check.rs +++ /dev/null @@ -1,5 +0,0 @@ -use actix_web::HttpResponse; - -pub async fn health_check() -> HttpResponse { - HttpResponse::Ok().finish() -} diff --git a/chapter04/src/routes/mod.rs b/chapter04/src/routes/mod.rs deleted file mode 100644 index 90ffeed..0000000 --- a/chapter04/src/routes/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod health_check; -mod subscriptions; - -pub use health_check::*; -pub use subscriptions::*; diff --git a/chapter04/tests/health_check.rs b/chapter04/tests/health_check.rs deleted file mode 100644 index c0ac230..0000000 --- a/chapter04/tests/health_check.rs +++ /dev/null @@ -1,143 +0,0 @@ -use chapter04::configuration::{get_configuration, DatabaseSettings}; -use chapter04::startup::run; -use chapter04::telemetry::{get_subscriber, init_subscriber}; -use sqlx::{Connection, Executor, PgConnection, PgPool}; -use std::net::TcpListener; -use uuid::Uuid; - -// Ensure that the `tracing` stack is only initialised once using `lazy_static` -lazy_static::lazy_static! { - static ref TRACING: () = { - let filter = if std::env::var("TEST_LOG").is_ok() { "debug" } else { "" }; - let subscriber = get_subscriber("test".into(), filter.into()); - init_subscriber(subscriber); - }; -} - -pub struct TestApp { - pub address: String, - pub db_pool: PgPool, -} - -async fn spawn_app() -> TestApp { - // The first time `initialize` is invoked the code in `TRACING` is executed. - // All other invocations will instead skip execution. - lazy_static::initialize(&TRACING); - - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); - // We retrieve the port assigned to us by the OS - let port = listener.local_addr().unwrap().port(); - let address = format!("http://127.0.0.1:{}", port); - - let mut configuration = get_configuration().expect("Failed to read configuration."); - configuration.database.database_name = Uuid::new_v4().to_string(); - let connection_pool = configure_database(&configuration.database).await; - - let server = run(listener, connection_pool.clone()).expect("Failed to bind address"); - let _ = tokio::spawn(server); - TestApp { - address, - db_pool: connection_pool, - } -} - -pub async fn configure_database(config: &DatabaseSettings) -> PgPool { - // Create database - let mut connection = PgConnection::connect(&config.connection_string_without_db()) - .await - .expect("Failed to connect to Postgres"); - connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) - .await - .expect("Failed to create database."); - - // Migrate database - let connection_pool = PgPool::connect(&config.connection_string()) - .await - .expect("Failed to connect to Postgres."); - sqlx::migrate!("./migrations") - .run(&connection_pool) - .await - .expect("Failed to migrate the database"); - - connection_pool -} - -#[actix_rt::test] -async fn health_check_works() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - - // Act - let response = client - // Use the returned application address - .get(&format!("{}/health_check", &app.address)) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert!(response.status().is_success()); - assert_eq!(Some(0), response.content_length()); -} - -#[actix_rt::test] -async fn subscribe_returns_a_200_for_valid_form_data() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; - - // Act - let response = client - .post(&format!("{}/subscriptions", &app.address)) - .header("Content-Type", "application/x-www-form-urlencoded") - .body(body) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert_eq!(200, response.status().as_u16()); - - let saved = sqlx::query!("SELECT email, name FROM subscriptions",) - .fetch_one(&app.db_pool) - .await - .expect("Failed to fetch saved subscription."); - - assert_eq!(saved.email, "ursula_le_guin@gmail.com"); - assert_eq!(saved.name, "le guin"); -} - -#[actix_rt::test] -async fn subscribe_returns_a_400_when_data_is_missing() { - // Arrange - let app = spawn_app().await; - let client = reqwest::Client::new(); - let test_cases = vec![ - ("name=le%20guin", "missing the email"), - ("email=ursula_le_guin%40gmail.com", "missing the name"), - ("", "missing both name and email"), - ]; - - for (invalid_body, error_message) in test_cases { - // Act - let response = client - .post(&format!("{}/subscriptions", &app.address)) - .header("Content-Type", "application/x-www-form-urlencoded") - .body(invalid_body) - .send() - .await - .expect("Failed to execute request."); - - // Assert - assert_eq!( - 400, - response.status().as_u16(), - // Additional customised error message on test failure - "The API did not fail with 400 Bad Request when the payload was {}.", - error_message - ); - } -} diff --git a/chapter05/.env b/chapter05/.env deleted file mode 100644 index 88cfb53..0000000 --- a/chapter05/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL="postgres://postgres:password@localhost:5432/newsletter" diff --git a/chapter05/Cargo.toml b/chapter05/Cargo.toml deleted file mode 100644 index 62cea9a..0000000 --- a/chapter05/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "chapter05" -version = "0.1.0" -authors = ["LukeMathWalker "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -path = "src/lib.rs" - -[[bin]] -path = "src/main.rs" -name = "chapter05" - -[dependencies] -actix-web = "3.0.0" -actix-rt = "1.1.1" -tokio = "0.2.22" -serde = "1.0.115" -config = { version = "0.10.1", default-features = false, features = ["yaml"] } -sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] } -uuid = { version = "0.8.1", features = ["v4"] } -chrono = "0.4.15" -tracing = "0.1.19" -tracing-futures = "0.2.4" -tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } -tracing-bunyan-formatter = "0.1.6" -tracing-log = "0.1.1" -tracing-actix-web = "0.2.0" -serde-aux = "1.0.1" - -[dev-dependencies] -reqwest = { version = "0.10.7", features = ["json"] } -lazy_static = "1.4.0" diff --git a/chapter05/migrations/20200823135036_create_subscriptions_table.sql b/chapter05/migrations/20200823135036_create_subscriptions_table.sql deleted file mode 100644 index 2c0d262..0000000 --- a/chapter05/migrations/20200823135036_create_subscriptions_table.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Create Subscriptions Table -CREATE TABLE subscriptions( - id uuid NOT NULL, - PRIMARY KEY (id), - email TEXT NOT NULL UNIQUE, - name TEXT NOT NULL, - subscribed_at timestamptz NOT NULL -); \ No newline at end of file diff --git a/chapter05/scripts/init_db.sh b/chapter05/scripts/init_db.sh deleted file mode 100755 index cc1df04..0000000 --- a/chapter05/scripts/init_db.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -set -x -set -eo pipefail - -# Check if a custom user has been set, otherwise default to 'postgres' -DB_USER=${POSTGRES_USER:=postgres} -# Check if a custom password has been set, otherwise default to 'password' -DB_PASSWORD="${POSTGRES_PASSWORD:=password}" -# Check if a custom password has been set, otherwise default to 'newsletter' -DB_NAME="${POSTGRES_DB:=newsletter}" -# Check if a custom port has been set, otherwise default to '5432' -DB_PORT="${POSTGRES_PORT:=5432}" - -# Allow to skip Docker if a dockerized Postgres database is already running -if [[ -z "${SKIP_DOCKER}" ]] -then - # Launch postgres using Docker - docker run \ - -e POSTGRES_USER=${DB_USER} \ - -e POSTGRES_PASSWORD=${DB_PASSWORD} \ - -e POSTGRES_DB=${DB_NAME} \ - -p "${DB_PORT}":5432 \ - -d postgres \ - postgres -N 1000 - # ^ Increased maximum number of connections for testing purposes -fi - -# Keep pinging Postgres until it's ready to accept commands -until PGPASSWORD="${DB_PASSWORD}" psql -h "localhost" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do - >&2 echo "Postgres is still unavailable - sleeping" - sleep 1 -done - ->&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!" - -export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME} -sqlx database create -sqlx migrate run - ->&2 echo "Postgres has been migrated, ready to go!" diff --git a/chapter05/src/lib.rs b/chapter05/src/lib.rs deleted file mode 100644 index 5d8e21e..0000000 --- a/chapter05/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(clippy::toplevel_ref_arg)] -pub mod configuration; -pub mod routes; -pub mod startup; -pub mod telemetry; diff --git a/chapter05/src/routes/health_check.rs b/chapter05/src/routes/health_check.rs deleted file mode 100644 index d7eb4e0..0000000 --- a/chapter05/src/routes/health_check.rs +++ /dev/null @@ -1,5 +0,0 @@ -use actix_web::HttpResponse; - -pub async fn health_check() -> HttpResponse { - HttpResponse::Ok().finish() -} diff --git a/chapter05/src/routes/mod.rs b/chapter05/src/routes/mod.rs deleted file mode 100644 index 90ffeed..0000000 --- a/chapter05/src/routes/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod health_check; -mod subscriptions; - -pub use health_check::*; -pub use subscriptions::*; diff --git a/chapter05/src/routes/subscriptions.rs b/chapter05/src/routes/subscriptions.rs deleted file mode 100644 index fd801af..0000000 --- a/chapter05/src/routes/subscriptions.rs +++ /dev/null @@ -1,52 +0,0 @@ -use actix_web::{web, HttpResponse}; -use chrono::Utc; -use sqlx::PgPool; -use uuid::Uuid; - -#[derive(serde::Deserialize)] -pub struct FormData { - email: String, - name: String, -} - -#[tracing::instrument( - name = "Adding a new subscriber", - skip(form, pool), - fields( - email = %form.email, - name = %form.name - ) -)] -pub async fn subscribe( - form: web::Form, - pool: web::Data, -) -> Result { - insert_subscriber(&pool, &form) - .await - .map_err(|_| HttpResponse::InternalServerError().finish())?; - Ok(HttpResponse::Ok().finish()) -} - -#[tracing::instrument( - name = "Saving new subscriber details in the database", - skip(form, pool) -)] -pub async fn insert_subscriber(pool: &PgPool, form: &FormData) -> Result<(), sqlx::Error> { - sqlx::query!( - r#" - INSERT INTO subscriptions (id, email, name, subscribed_at) - VALUES ($1, $2, $3, $4) - "#, - Uuid::new_v4(), - form.email, - form.name, - Utc::now() - ) - .execute(pool) - .await - .map_err(|e| { - tracing::error!("Failed to execute query: {:?}", e); - e - })?; - Ok(()) -} diff --git a/chapter05/src/startup.rs b/chapter05/src/startup.rs deleted file mode 100644 index d091772..0000000 --- a/chapter05/src/startup.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::routes::{health_check, subscribe}; -use actix_web::dev::Server; -use actix_web::web::Data; -use actix_web::{web, App, HttpServer}; -use sqlx::PgPool; -use std::net::TcpListener; -use tracing_actix_web::TracingLogger; - -pub fn run(listener: TcpListener, db_pool: PgPool) -> Result { - let db_pool = Data::new(db_pool); - let server = HttpServer::new(move || { - App::new() - .wrap(TracingLogger) - .route("/health_check", web::get().to(health_check)) - .route("/subscriptions", web::post().to(subscribe)) - .app_data(db_pool.clone()) - }) - .listen(listener)? - .run(); - Ok(server) -} diff --git a/chapter05/src/telemetry.rs b/chapter05/src/telemetry.rs deleted file mode 100644 index 27168fb..0000000 --- a/chapter05/src/telemetry.rs +++ /dev/null @@ -1,29 +0,0 @@ -use tracing::subscriber::set_global_default; -use tracing::Subscriber; -use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; -use tracing_log::LogTracer; -use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; - -/// Compose multiple layers into a `tracing`'s subscriber. -/// -/// # Implementation Notes -/// -/// We are using `impl Subscriber` as return type to avoid having to spell out the actual -/// type of the returned subscriber, which is indeed quite complex. -pub fn get_subscriber(name: String, env_filter: String) -> impl Subscriber + Sync + Send { - let env_filter = - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter)); - let formatting_layer = BunyanFormattingLayer::new(name, std::io::stdout); - Registry::default() - .with(env_filter) - .with(JsonStorageLayer) - .with(formatting_layer) -} - -/// Register a subscriber as global default to process span data. -/// -/// It should only be called once! -pub fn init_subscriber(subscriber: impl Subscriber + Sync + Send) { - LogTracer::init().expect("Failed to set logger"); - set_global_default(subscriber).expect("Failed to set subscriber"); -} diff --git a/chapter05/configuration/base.yaml b/configuration/base.yaml similarity index 100% rename from chapter05/configuration/base.yaml rename to configuration/base.yaml diff --git a/chapter05/configuration/local.yaml b/configuration/local.yaml similarity index 100% rename from chapter05/configuration/local.yaml rename to configuration/local.yaml diff --git a/chapter05/configuration/production.yaml b/configuration/production.yaml similarity index 100% rename from chapter05/configuration/production.yaml rename to configuration/production.yaml diff --git a/chapter03-1/migrations/20200823135036_create_subscriptions_table.sql b/migrations/20200823135036_create_subscriptions_table.sql similarity index 100% rename from chapter03-1/migrations/20200823135036_create_subscriptions_table.sql rename to migrations/20200823135036_create_subscriptions_table.sql diff --git a/chapter03-1/scripts/init_db.sh b/scripts/init_db.sh similarity index 100% rename from chapter03-1/scripts/init_db.sh rename to scripts/init_db.sh diff --git a/chapter05/spec.yaml b/spec.yaml similarity index 95% rename from chapter05/spec.yaml rename to spec.yaml index c2c0870..52c15fa 100644 --- a/chapter05/spec.yaml +++ b/spec.yaml @@ -6,8 +6,8 @@ region: fra services: - name: zero2prod # Relative to the repository root - dockerfile_path: chapter05/Dockerfile - source_dir: chapter05 + dockerfile_path: Dockerfile + source_dir: . github: branch: ch-05 deploy_on_push: true diff --git a/chapter05/sqlx-data.json b/sqlx-data.json similarity index 100% rename from chapter05/sqlx-data.json rename to sqlx-data.json diff --git a/chapter05/src/configuration.rs b/src/configuration.rs similarity index 100% rename from chapter05/src/configuration.rs rename to src/configuration.rs diff --git a/chapter04/src/lib.rs b/src/lib.rs similarity index 100% rename from chapter04/src/lib.rs rename to src/lib.rs diff --git a/chapter05/src/main.rs b/src/main.rs similarity index 84% rename from chapter05/src/main.rs rename to src/main.rs index 102e50a..b89442a 100644 --- a/chapter05/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ -use chapter05::configuration::get_configuration; -use chapter05::startup::run; -use chapter05::telemetry::{get_subscriber, init_subscriber}; use sqlx::postgres::PgPoolOptions; use std::net::TcpListener; +use zero2prod::configuration::get_configuration; +use zero2prod::startup::run; +use zero2prod::telemetry::{get_subscriber, init_subscriber}; #[actix_rt::main] async fn main() -> std::io::Result<()> { diff --git a/chapter03-1/src/routes/health_check.rs b/src/routes/health_check.rs similarity index 100% rename from chapter03-1/src/routes/health_check.rs rename to src/routes/health_check.rs diff --git a/chapter03-1/src/routes/mod.rs b/src/routes/mod.rs similarity index 100% rename from chapter03-1/src/routes/mod.rs rename to src/routes/mod.rs diff --git a/chapter04/src/routes/subscriptions.rs b/src/routes/subscriptions.rs similarity index 100% rename from chapter04/src/routes/subscriptions.rs rename to src/routes/subscriptions.rs diff --git a/chapter04/src/startup.rs b/src/startup.rs similarity index 100% rename from chapter04/src/startup.rs rename to src/startup.rs diff --git a/chapter04/src/telemetry.rs b/src/telemetry.rs similarity index 100% rename from chapter04/src/telemetry.rs rename to src/telemetry.rs diff --git a/chapter05/tests/health_check.rs b/tests/health_check.rs similarity index 96% rename from chapter05/tests/health_check.rs rename to tests/health_check.rs index 11db352..8c5ca8e 100644 --- a/chapter05/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,9 +1,9 @@ -use chapter05::configuration::{get_configuration, DatabaseSettings}; -use chapter05::startup::run; -use chapter05::telemetry::{get_subscriber, init_subscriber}; use sqlx::{Connection, Executor, PgConnection, PgPool}; use std::net::TcpListener; use uuid::Uuid; +use zero2prod::configuration::{get_configuration, DatabaseSettings}; +use zero2prod::startup::run; +use zero2prod::telemetry::{get_subscriber, init_subscriber}; // Ensure that the `tracing` stack is only initialised once using `lazy_static` lazy_static::lazy_static! { From f4ae2d2bab499f3a98c2b9666f8d398b9719abe7 Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Sat, 5 Dec 2020 17:27:47 +0000 Subject: [PATCH 02/10] Update README. --- README.md | 36 +++++++++++++++++++++++++++--------- spec.yaml | 2 +- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 683da91..9c9a79e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Zero To Production / Code +# Zero To Production / Code (Chapter 5)
@@ -6,15 +6,33 @@ This repository serves as supplementary material for [the book](https://zero2prod.com/): it hosts snapshots of the codebase of our email newsletter project at end of each chapter. -It is structured as a `cargo` workspace: running `cargo build` will build the codebase for **all** chapters. -If you want to build/test/run the code for a _specific_ chapter, just move into its folder! E.g.: +**This branch is a snapshot of the project at the end of Chapter 5.** + +## Pre-requisite + +You'll need to install: + +- [Rust](https://www.rust-lang.org/tools/install) +- [Docker](https://docs.docker.com/get-docker/) + +Launch a (migrated) Postgres database via Docker: + ```bash -# Run tests for the first part of Chapter 3 -cd chapter03-0 -cargo test +./scripts/init_db.sh ``` -Alternatively, from the top-level folder, you can specify the binary you are interested into: + +## How to build + +Using `cargo`: + ```bash -# Run the application as it is at end of the first part of Chapter 3 -cargo run --bin chapter03-0 +cargo build +``` + +## How to test + +Using `cargo`: + +```bash +cargo test ``` diff --git a/spec.yaml b/spec.yaml index 52c15fa..79076f5 100644 --- a/spec.yaml +++ b/spec.yaml @@ -9,7 +9,7 @@ services: dockerfile_path: Dockerfile source_dir: . github: - branch: ch-05 + branch: root-chapter05 deploy_on_push: true repo: LukeMathWalker/zero-to-production # Active probe used by DigitalOcean's to ensure our application is healthy From 847bb8152a4113c7208f83231512c4819cdf581a Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Sat, 5 Dec 2020 19:10:01 +0000 Subject: [PATCH 03/10] Update CI + update sqlx --- .github/workflows/general.yml | 36 +- Cargo.lock | 796 ++++++++++++++++++++++------------ Cargo.toml | 2 +- 3 files changed, 534 insertions(+), 300 deletions(-) diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index f7207a7..a7cb2ba 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -4,6 +4,9 @@ on: # NB: this differs from the book's project! # These settings allow us to run this specific CI pipeline for PRs against # this specific branch (a.k.a. book chapter). + push: + branches: + - root-chapter-05 pull_request: types: [ opened, synchronize, reopened ] branches: @@ -18,7 +21,11 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres + image: postgres:12 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: postgres ports: - 5432:5432 steps: @@ -31,8 +38,8 @@ jobs: - name: Migrate database run: | sudo apt-get install libpq-dev -y - cargo install --version=0.1.0-beta.1 sqlx-cli --no-default-features --features postgres - SKIP_DOCKER=true cd chapter03-1 && ./scripts/init_db.sh + cargo install --version=0.2.0 sqlx-cli --no-default-features --features postgres + SKIP_DOCKER=true ./scripts/init_db.sh - uses: actions-rs/cargo@v1 with: command: test @@ -58,7 +65,11 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres + image: postgres:12 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: postgres ports: - 5432:5432 steps: @@ -72,8 +83,8 @@ jobs: - name: Migrate database run: | sudo apt-get install libpq-dev -y - cargo install --version=0.1.0-beta.1 sqlx-cli --no-default-features --features postgres - SKIP_DOCKER=true cd chapter03-1 && ./scripts/init_db.sh + cargo install --version=0.2.0 sqlx-cli --no-default-features --features postgres + SKIP_DOCKER=true ./scripts/init_db.sh - uses: actions-rs/cargo@v1 with: command: clippy @@ -84,7 +95,11 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres + image: postgres:12 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: postgres ports: - 5432:5432 steps: @@ -100,10 +115,9 @@ jobs: - name: Migrate database run: | sudo apt-get install libpq-dev -y - cargo install --version=0.1.0-beta.1 sqlx-cli --no-default-features --features postgres - SKIP_DOCKER=true cd chapter03-1 && ./scripts/init_db.sh - + cargo install --version=0.2.0 sqlx-cli --no-default-features --features postgres + SKIP_DOCKER=true ./scripts/init_db.sh - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 with: - args: '--ignore-tests' \ No newline at end of file + args: '--ignore-tests' diff --git a/Cargo.lock b/Cargo.lock index 7b76292..1f1cd45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,7 +11,7 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project", + "pin-project 0.4.27", "tokio", "tokio-util", ] @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "2.0.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05dd80ba8f27c4a34357c07e338c8f5c38f8520e6d626ca1727d8fecc41b0cab" +checksum = "452299e87817ae5673910e53c243484ca38be3828db819b6011736fc6982e874" dependencies = [ "actix-codec", "actix-connect", @@ -47,7 +47,7 @@ dependencies = [ "actix-service", "actix-threadpool", "actix-utils", - "base64", + "base64 0.13.0", "bitflags", "brotli2", "bytes", @@ -71,7 +71,7 @@ dependencies = [ "log", "mime", "percent-encoding", - "pin-project", + "pin-project 1.0.2", "rand", "regex", "serde", @@ -79,14 +79,14 @@ dependencies = [ "serde_urlencoded", "sha-1", "slab", - "time 0.2.22", + "time 0.2.23", ] [[package]] name = "actix-macros" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a60f9ba7c4e6df97f3aacb14bb5c0cd7d98a49dcbaed0d7f292912ad9a6a3ed2" +checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" dependencies = [ "quote", "syn", @@ -147,7 +147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" dependencies = [ "futures-util", - "pin-project", + "pin-project 0.4.27", ] [[package]] @@ -207,15 +207,15 @@ dependencies = [ "futures-sink", "futures-util", "log", - "pin-project", + "pin-project 0.4.27", "slab", ] [[package]] name = "actix-web" -version = "3.1.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b12fe25e11cd9ed2ef2e428427eb6178a1b363f3f7f0dab8278572f11b2da1" +checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86" dependencies = [ "actix-codec", "actix-http", @@ -239,22 +239,22 @@ dependencies = [ "fxhash", "log", "mime", - "pin-project", + "pin-project 1.0.2", "regex", "serde", "serde_json", "serde_urlencoded", "socket2", - "time 0.2.22", - "tinyvec 1.0.1", + "time 0.2.23", + "tinyvec", "url", ] [[package]] name = "actix-web-codegen" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "750ca8fb60bbdc79491991650ba5d2ae7cd75f3fc00ead51390cfe9efda0d4d8" +checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" dependencies = [ "proc-macro2", "quote", @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" dependencies = [ "gimli", ] @@ -278,15 +278,19 @@ checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" [[package]] name = "ahash" -version = "0.3.8" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +checksum = "ad4243ec6feddc812c0f442d9765374d250aba94e10ecf8b632e1b1c118547e8" +dependencies = [ + "getrandom 0.2.0", + "lazy_static", +] [[package]] name = "aho-corasick" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -300,23 +304,17 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "arc-swap" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" - [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "async-trait" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" dependencies = [ "proc-macro2", "quote", @@ -325,9 +323,9 @@ dependencies = [ [[package]] name = "atoi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0afb7287b68575f5ca0e5c7e40191cbd4be59d325781f46faa603e176eaef47" +checksum = "5c897df197d57c25b37df9d8fa2f93ddbfeee9ebd2264350ac79c8ec4b795885" dependencies = [ "num-traits", ] @@ -340,16 +338,17 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "awc" -version = "2.0.0" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "150e00c06683ab44c5f97d033950e5d87a7a042d06d77f5eecb443cbd23d0575" +checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691" dependencies = [ "actix-codec", "actix-http", "actix-rt", "actix-service", - "base64", + "base64 0.13.0", "bytes", + "cfg-if 1.0.0", "derive_more", "futures-core", "log", @@ -363,9 +362,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e" +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" dependencies = [ "addr2line", "cfg-if 1.0.0", @@ -377,9 +376,9 @@ dependencies = [ [[package]] name = "base-x" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" [[package]] name = "base64" @@ -387,12 +386,30 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -457,9 +474,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.61" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -486,15 +503,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -dependencies = [ - "bitflags", -] - [[package]] name = "config" version = "0.10.1" @@ -502,25 +510,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" dependencies = [ "lazy_static", - "nom", + "nom 5.1.2", "serde", "yaml-rust", ] [[package]] -name = "const_fn" -version = "0.4.2" +name = "console_error_panic_hook" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + +[[package]] +name = "const_fn" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" [[package]] name = "cookie" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1373a16a4937bc34efec7b391f9c1500c30b8478a701a4f44c9165cc0475a6e0" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" dependencies = [ "percent-encoding", - "time 0.2.22", + "time 0.2.23", "version_check", ] @@ -532,9 +550,9 @@ checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" [[package]] name = "core-foundation" -version = "0.7.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" dependencies = [ "core-foundation-sys", "libc", @@ -542,9 +560,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" [[package]] name = "cpuid-bool" @@ -563,11 +581,11 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -604,9 +622,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" dependencies = [ "generic-array", "subtle", @@ -644,12 +662,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "dtoa" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" - [[package]] name = "either" version = "1.6.1" @@ -661,11 +673,11 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -682,11 +694,11 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee" +checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "crc32fast", "libc", "miniz_oxide", @@ -713,6 +725,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -730,10 +752,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] -name = "futures" -version = "0.3.6" +name = "funty" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e" +checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8" + +[[package]] +name = "futures" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" dependencies = [ "futures-channel", "futures-core", @@ -746,9 +774,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" dependencies = [ "futures-core", "futures-sink", @@ -756,15 +784,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" [[package]] name = "futures-executor" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab" +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" dependencies = [ "futures-core", "futures-task", @@ -773,15 +801,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" [[package]] name = "futures-macro" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -791,24 +819,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" [[package]] name = "futures-task" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" dependencies = [ "futures-channel", "futures-core", @@ -817,7 +845,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project", + "pin-project 1.0.2", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -833,6 +861,19 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generator" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi 0.3.9", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -865,16 +906,27 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.22.0" +name = "getrandom" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "h2" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" dependencies = [ "bytes", "fnv", @@ -887,16 +939,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" -dependencies = [ - "ahash", - "autocfg", + "tracing-futures", ] [[package]] @@ -931,9 +974,9 @@ checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "hmac" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" dependencies = [ "crypto-mac", "digest", @@ -985,9 +1028,9 @@ checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "hyper" -version = "0.13.8" +version = "0.13.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" dependencies = [ "bytes", "futures-channel", @@ -999,7 +1042,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project", + "pin-project 1.0.2", "socket2", "tokio", "tower-service", @@ -1038,16 +1081,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", - "hashbrown 0.9.1", + "hashbrown", ] [[package]] name = "instant" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] @@ -1085,9 +1128,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "js-sys" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ "wasm-bindgen", ] @@ -1129,9 +1172,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "linked-hash-map" @@ -1141,9 +1184,9 @@ checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ "scopeguard", ] @@ -1157,6 +1200,19 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "loom" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed" +dependencies = [ + "cfg-if 0.1.10", + "generator", + "scoped-tls", + "serde", + "serde_json", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -1212,9 +1268,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "mime" @@ -1244,9 +1300,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.22" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ "cfg-if 0.1.10", "fuchsia-zircon", @@ -1274,9 +1330,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", @@ -1286,9 +1342,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f" dependencies = [ "lazy_static", "libc", @@ -1304,9 +1360,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +checksum = "d7cf75f38f16cb05ea017784dc6dbfd354f76c223dba37701734c4f5a9337d02" dependencies = [ "cfg-if 0.1.10", "libc", @@ -1325,10 +1381,22 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.43" +name = "nom" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0" +dependencies = [ + "bitvec", + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -1336,9 +1404,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] @@ -1355,15 +1423,15 @@ dependencies = [ [[package]] name = "object" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" [[package]] name = "once_cell" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" @@ -1406,9 +1474,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api", @@ -1417,12 +1485,11 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall", @@ -1442,7 +1509,16 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" dependencies = [ - "pin-project-internal", + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +dependencies = [ + "pin-project-internal 1.0.2", ] [[package]] @@ -1457,10 +1533,27 @@ dependencies = [ ] [[package]] -name = "pin-project-lite" -version = "0.1.10" +name = "pin-project-internal" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" [[package]] name = "pin-utils" @@ -1476,15 +1569,15 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "ppv-lite86" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro-hack" -version = "0.5.18" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" @@ -1516,13 +1609,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.15", "libc", "rand_chacha", "rand_core", @@ -1545,7 +1644,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.15", ] [[package]] @@ -1565,9 +1664,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", @@ -1587,9 +1686,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "remove_dir_all" @@ -1602,11 +1701,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e" +checksum = "fb15d6255c792356a0f578d8a645c677904dc02e862bebe2ecc18e0c01b9a0ce" dependencies = [ - "base64", + "base64 0.13.0", "bytes", "encoding_rs", "futures-core", @@ -1623,7 +1722,7 @@ dependencies = [ "mime_guess", "native-tls", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.0", "serde", "serde_json", "serde_urlencoded", @@ -1632,25 +1731,41 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", "web-sys", "winreg 0.7.0", ] [[package]] name = "resolv-conf" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", "quick-error", ] [[package]] -name = "rustc-demangle" -version = "0.1.17" +name = "ring" +version = "0.16.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2610b7f643d18c87dff3b489950269617e6601a51f1f05aa5daefee36f64f0b" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" [[package]] name = "rustc_version" @@ -1661,6 +1776,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +dependencies = [ + "base64 0.12.3", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "ryu" version = "1.0.5" @@ -1677,6 +1805,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1684,10 +1818,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "security-framework" -version = "0.4.4" +name = "sct" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" dependencies = [ "bitflags", "core-foundation", @@ -1698,9 +1842,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "0.4.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" dependencies = [ "core-foundation-sys", "libc", @@ -1732,9 +1876,9 @@ dependencies = [ [[package]] name = "serde-aux" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb1c0e382599b35cf66986b74182d3787605bd4c3087b4091ee305a692f071" +checksum = "905f2fc9f3d1574e8b5923a58118240021f01d4e239673937ffb9f42707a4f22" dependencies = [ "chrono", "serde", @@ -1754,9 +1898,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" dependencies = [ "indexmap", "itoa", @@ -1766,24 +1910,24 @@ dependencies = [ [[package]] name = "serde_urlencoded" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" dependencies = [ - "dtoa", + "form_urlencoded", "itoa", + "ryu", "serde", - "url", ] [[package]] name = "sha-1" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" dependencies = [ "block-buffer", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "cpuid-bool", "digest", "opaque-debug", @@ -1797,12 +1941,12 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" dependencies = [ "block-buffer", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "cpuid-bool", "digest", "opaque-debug", @@ -1810,20 +1954,20 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.0.9" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e" +checksum = "7b4921be914e16899a80adefb821f8ddb7974e3f1250223575a44ed994882127" dependencies = [ "lazy_static", + "loom", ] [[package]] name = "signal-hook-registry" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" dependencies = [ - "arc-swap", "libc", ] @@ -1835,38 +1979,46 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "smallvec" -version = "1.4.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" [[package]] name = "socket2" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +checksum = "2c29947abdee2a218277abeca306f25789c938e500ea5a9d4b12a5a504466902" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "redox_syscall", "winapi 0.3.9", ] [[package]] -name = "sqlformat" -version = "0.1.1" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f699301eec598ffd6c39832cca1416381ea459ac73c506f6ca74c8750fb52969" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "sqlformat" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c70f0235b9925cbb106c52af1a28b5ea4885a8b851e328b8562e257a389c2d" dependencies = [ "lazy_static", "maplit", + "nom 6.0.1", "regex", + "unicode_categories", ] [[package]] name = "sqlx" -version = "0.4.0-beta.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb7b012f28c74075d6b11172ba1874f4376a255509462eaf2ef25068b31729f" +checksum = "d1f8eb788e1733bdbf69a8f97087213ebdebd253d4782c686d3cfd586b0a9453" dependencies = [ "sqlx-core", "sqlx-macros", @@ -1874,12 +2026,13 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.4.0-beta.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2857d90b39b8528948109abc1b8d4c1905d184c87deaf06055f0b21050f13e" +checksum = "5e647268dc1239dd9db2d3103fefd61151971a2214882cff9efea6f60cf50840" dependencies = [ + "ahash", "atoi", - "base64", + "base64 0.13.0", "bitflags", "byteorder", "bytes", @@ -1892,7 +2045,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "hashbrown 0.8.2", "hex", "hmac", "itoa", @@ -1905,6 +2057,7 @@ dependencies = [ "parking_lot", "percent-encoding", "rand", + "rustls", "serde", "sha-1", "sha2", @@ -1915,14 +2068,16 @@ dependencies = [ "thiserror", "url", "uuid", + "webpki", + "webpki-roots", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.4.0-beta.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1bc862e5f4484965156c224f7e4c139f2d3c65b6aa0e3ae8c63461831b8da9" +checksum = "c7acd32cba35531345f8a94a038874baf00efd0b701c913f5b00d2870b474b64" dependencies = [ "dotenv", "either", @@ -1942,21 +2097,22 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f9104f6116b568358f315e9839ae66c4ebbc0e974db5580105f0acfeb4863f" +checksum = "63fc5454c9dd7aaea3a0eeeb65ca40d06d0d8e7413a8184f7c3a3ffa5056190b" dependencies = [ - "native-tls", + "actix-rt", + "actix-threadpool", "once_cell", "tokio", - "tokio-native-tls", + "tokio-rustls", ] [[package]] name = "standback" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e0831040d2cf2bdfd51b844be71885783d489898a192f254ae25d57cce725c" +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" dependencies = [ "version_check", ] @@ -2034,15 +2190,21 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" [[package]] name = "syn" -version = "1.0.45" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556" +checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e" + [[package]] name = "tempfile" version = "3.1.0" @@ -2059,18 +2221,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" dependencies = [ "proc-macro2", "quote", @@ -2108,9 +2270,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55b7151c9065e80917fbf285d9a5d1432f60db41d170ccafc749a136b41a93af" +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" dependencies = [ "const_fn", "libc", @@ -2146,15 +2308,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "0.3.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" - -[[package]] -name = "tinyvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" dependencies = [ "tinyvec_macros", ] @@ -2167,9 +2323,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +checksum = "a6d7ad61edd59bfcc7e80dababf0f4aed2e6d5e0ba1659356ae889752dfc12ff" dependencies = [ "bytes", "fnv", @@ -2181,7 +2337,7 @@ dependencies = [ "mio", "mio-uds", "num_cpus", - "pin-project-lite", + "pin-project-lite 0.1.11", "signal-hook-registry", "slab", "tokio-macros", @@ -2190,9 +2346,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2", "quote", @@ -2200,13 +2356,15 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.1.0" +name = "tokio-rustls" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd608593a919a8e05a7d1fc6df885e40f6a88d3a70a3a7eff23ff27964eda069" +checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" dependencies = [ - "native-tls", + "futures-core", + "rustls", "tokio", + "webpki", ] [[package]] @@ -2229,7 +2387,7 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite", + "pin-project-lite 0.1.11", "tokio", ] @@ -2241,13 +2399,13 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "log", - "pin-project-lite", + "pin-project-lite 0.2.0", "tracing-attributes", "tracing-core", ] @@ -2278,9 +2436,9 @@ dependencies = [ [[package]] name = "tracing-bunyan-formatter" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8dfd28e9ee6d79937f139ae4f112fa2172018cfdf111c0dac1f3f8c6912053" +checksum = "06718867c20ea03700d41a9413610cccf5d772caea792f34cc73cdd43f0e14a6" dependencies = [ "chrono", "gethostname", @@ -2308,7 +2466,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" dependencies = [ - "pin-project", + "pin-project 0.4.27", "tracing", ] @@ -2335,9 +2493,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd" +checksum = "a1fa8f0c8f4c594e4fc9debc1990deab13238077271ba84dd853d54902ee3401" dependencies = [ "ansi_term", "chrono", @@ -2357,9 +2515,9 @@ dependencies = [ [[package]] name = "trust-dns-proto" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd7061ba6f4d4d9721afedffbfd403f20f39a4301fee1b70d6fcd09cca69f28" +checksum = "53861fcb288a166aae4c508ae558ed18b53838db728d4d310aad08270a7d4c2b" dependencies = [ "async-trait", "backtrace", @@ -2377,9 +2535,9 @@ dependencies = [ [[package]] name = "trust-dns-resolver" -version = "0.19.5" +version = "0.19.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23cdfdc3d8300b3c50c9e84302d3bd6d860fb9529af84ace6cf9665f181b77" +checksum = "6759e8efc40465547b0dfce9500d733c65f969a4cbbfbe3ccf68daaa46ef179e" dependencies = [ "backtrace", "cfg-if 0.1.10", @@ -2427,18 +2585,18 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ - "tinyvec 0.3.4", + "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-xid" @@ -2447,11 +2605,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] -name = "url" -version = "2.1.1" +name = "unicode_categories" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ + "form_urlencoded", "idna", "matches", "percent-encoding", @@ -2502,11 +2673,11 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "serde", "serde_json", "wasm-bindgen-macro", @@ -2514,9 +2685,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ "bumpalo", "lazy_static", @@ -2529,11 +2700,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da" +checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2541,9 +2712,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2551,9 +2722,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2", "quote", @@ -2564,20 +2735,63 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0355fa0c1f9b792a09b6dcb6a8be24d51e71e6d74972f9eb4a44c4c004d24a25" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07b46b98024c2ba2f9e83a10c2ef0515f057f2da299c1762a2017de80438b" +dependencies = [ + "proc-macro2", + "quote", +] [[package]] name = "web-sys" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ "js-sys", "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" +dependencies = [ + "webpki", +] + [[package]] name = "whoami" version = "0.9.0" @@ -2652,6 +2866,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "yaml-rust" version = "0.4.4" diff --git a/Cargo.toml b/Cargo.toml index 278d87c..d162c25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ actix-rt = "1.1.1" tokio = "0.2.22" serde = "1.0.115" config = { version = "0.10.1", default-features = false, features = ["yaml"] } -sqlx = { version = "0.4.0-beta.1", default-features = false, features = [ "runtime-tokio", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] } +sqlx = { version = "0.4.1", default-features = false, features = [ "runtime-actix-rustls", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] } uuid = { version = "0.8.1", features = ["v4"] } chrono = "0.4.15" tracing = "0.1.19" From e5a32a51a470c49b6ce7f1da01e1380ee65b8293 Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Tue, 8 Dec 2020 11:42:05 +0000 Subject: [PATCH 04/10] First integration test. --- tests/health_check.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/health_check.rs b/tests/health_check.rs index 8c5ca8e..b3f1bd0 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -141,3 +141,34 @@ async fn subscribe_returns_a_400_when_data_is_missing() { ); } } + +#[actix_rt::test] +async fn subscribe_returns_a_200_when_fields_are_present_but_empty() { + // Arrange + let app = spawn_app().await; + let client = reqwest::Client::new(); + let test_cases = vec![ + ("name=&email=ursula_le_guin%40gmail.com", "empty name"), + ("name=Ursula&email=", "empty email"), + ("name=Ursula&email=definitely-not-an-email", "invalid email"), + ]; + + for (body, description) in test_cases { + // Act + let response = client + .post(&format!("{}/subscriptions", &app.address)) + .header("Content-Type", "application/x-www-form-urlencoded") + .body(body) + .send() + .await + .expect("Failed to execute request."); + + // Assert + assert_eq!( + 200, + response.status().as_u16(), + "The API did not return a 200 OK when the payload was {}.", + description + ); + } +} From 2953d329ff9a914e9d7a7f3b6191a3f2d2e69f1b Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Wed, 9 Dec 2020 16:01:36 +0000 Subject: [PATCH 05/10] Add SubscriberName (no methods) --- src/domain.rs | 1 + src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 src/domain.rs diff --git a/src/domain.rs b/src/domain.rs new file mode 100644 index 0000000..8f37567 --- /dev/null +++ b/src/domain.rs @@ -0,0 +1 @@ +pub struct SubscriberName(String); diff --git a/src/lib.rs b/src/lib.rs index 5d8e21e..19fce70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ -#![allow(clippy::toplevel_ref_arg)] pub mod configuration; +pub mod domain; pub mod routes; pub mod startup; pub mod telemetry; From 0eaf310f292a610c502cac92f4957af66c0b4a82 Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Wed, 9 Dec 2020 17:20:15 +0000 Subject: [PATCH 06/10] Add SubscriberName constructor. --- Cargo.lock | 1 + Cargo.toml | 1 + src/domain.rs | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1f1cd45..b947c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2901,5 +2901,6 @@ dependencies = [ "tracing-futures", "tracing-log", "tracing-subscriber", + "unicode-segmentation", "uuid", ] diff --git a/Cargo.toml b/Cargo.toml index d162c25..044e166 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ tracing-bunyan-formatter = "0.1.6" tracing-log = "0.1.1" tracing-actix-web = "0.2.0" serde-aux = "1.0.1" +unicode-segmentation = "1.7.1" [dev-dependencies] reqwest = { version = "0.10.7", features = ["json"] } diff --git a/src/domain.rs b/src/domain.rs index 8f37567..df654a7 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1 +1,39 @@ +use unicode_segmentation::UnicodeSegmentation; + pub struct SubscriberName(String); + +impl SubscriberName { + /// Returns an instance of `SubscriberName` if the input satisfies all + /// our validation constraints on subscriber names. + /// It panics otherwise. + pub fn parse(s: String) -> SubscriberName { + // `.trim()` returns a view over the input `s` without trailing + // whitespace-like characters. + // `.is_empty` checks if the view contains any character. + let is_empty_or_whitespace = s.trim().is_empty(); + + // A grapheme is defined by the Unicode standard as a "user-perceived" + // character: `å` is a single grapheme, but it is composed of two characters + // (`a` and `̊`). + // + // `graphemes` returns an iterator over the graphemes in the input `s`. + // `true` specifies that we want to use the extended grapheme definition set, + // the recommended one. + let is_too_long = s.graphemes(true).count() > 256; + + // Iterate over all characters in the input `s` to check if any of them matches + // one of the characters in the forbidden array. + let forbidden_characters = ['/', '(', ')', '"', '<', '>', '\\', '{', '}']; + let contains_forbidden_characters = s + .chars() + .filter(|g| forbidden_characters.contains(g)) + .count() + > 0; + + if is_empty_or_whitespace || is_too_long || contains_forbidden_characters { + panic!(format!("{} is not a valid subscriber name.", s)) + } else { + Self(s) + } + } +} From 1c27412280040e217c7e19eaaa6c26c438a78ef5 Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Wed, 9 Dec 2020 22:55:00 +0000 Subject: [PATCH 07/10] Use `AsRef` --- src/domain.rs | 11 +++++++++++ src/routes/subscriptions.rs | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/domain.rs b/src/domain.rs index df654a7..c42f4cb 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1,5 +1,10 @@ use unicode_segmentation::UnicodeSegmentation; +pub struct NewSubscriber { + pub email: String, + pub name: SubscriberName, +} + pub struct SubscriberName(String); impl SubscriberName { @@ -37,3 +42,9 @@ impl SubscriberName { } } } + +impl AsRef for SubscriberName { + fn as_ref(&self) -> &str { + &self.0 + } +} diff --git a/src/routes/subscriptions.rs b/src/routes/subscriptions.rs index fd801af..2d3ea6e 100644 --- a/src/routes/subscriptions.rs +++ b/src/routes/subscriptions.rs @@ -1,3 +1,4 @@ +use crate::domain::{NewSubscriber, SubscriberName}; use actix_web::{web, HttpResponse}; use chrono::Utc; use sqlx::PgPool; @@ -21,7 +22,11 @@ pub async fn subscribe( form: web::Form, pool: web::Data, ) -> Result { - insert_subscriber(&pool, &form) + let new_subscriber = NewSubscriber { + email: form.0.email, + name: SubscriberName::parse(form.0.name), + }; + insert_subscriber(&pool, &new_subscriber) .await .map_err(|_| HttpResponse::InternalServerError().finish())?; Ok(HttpResponse::Ok().finish()) @@ -29,17 +34,20 @@ pub async fn subscribe( #[tracing::instrument( name = "Saving new subscriber details in the database", - skip(form, pool) + skip(new_subscriber, pool) )] -pub async fn insert_subscriber(pool: &PgPool, form: &FormData) -> Result<(), sqlx::Error> { +pub async fn insert_subscriber( + pool: &PgPool, + new_subscriber: &NewSubscriber, +) -> Result<(), sqlx::Error> { sqlx::query!( r#" INSERT INTO subscriptions (id, email, name, subscribed_at) VALUES ($1, $2, $3, $4) "#, Uuid::new_v4(), - form.email, - form.name, + new_subscriber.email, + new_subscriber.name.as_ref(), Utc::now() ) .execute(pool) From 56ee4e7746ef3951e9efa97b5dad4562e9e617eb Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Fri, 11 Dec 2020 14:24:11 +0000 Subject: [PATCH 08/10] Use Result --- src/domain.rs | 4 ++-- src/routes/subscriptions.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/domain.rs b/src/domain.rs index c42f4cb..5961b46 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -11,7 +11,7 @@ impl SubscriberName { /// Returns an instance of `SubscriberName` if the input satisfies all /// our validation constraints on subscriber names. /// It panics otherwise. - pub fn parse(s: String) -> SubscriberName { + pub fn parse(s: String) -> Result { // `.trim()` returns a view over the input `s` without trailing // whitespace-like characters. // `.is_empty` checks if the view contains any character. @@ -38,7 +38,7 @@ impl SubscriberName { if is_empty_or_whitespace || is_too_long || contains_forbidden_characters { panic!(format!("{} is not a valid subscriber name.", s)) } else { - Self(s) + Ok(Self(s)) } } } diff --git a/src/routes/subscriptions.rs b/src/routes/subscriptions.rs index 2d3ea6e..6e8715c 100644 --- a/src/routes/subscriptions.rs +++ b/src/routes/subscriptions.rs @@ -24,7 +24,7 @@ pub async fn subscribe( ) -> Result { let new_subscriber = NewSubscriber { email: form.0.email, - name: SubscriberName::parse(form.0.name), + name: SubscriberName::parse(form.0.name).expect("Name validation failed."), }; insert_subscriber(&pool, &new_subscriber) .await From 9dd3b0590a1366aa135f00a87bd67351ab841afb Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Fri, 11 Dec 2020 15:50:09 +0000 Subject: [PATCH 09/10] Return 400 on bad names. --- Cargo.lock | 10 ++++++++ Cargo.toml | 1 + src/domain.rs | 47 ++++++++++++++++++++++++++++++++++++- src/routes/subscriptions.rs | 4 +++- 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b947c80..1e66c55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "claim" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ad37958d55b29a7088909368968d2fe876a24c203f8441195130f3b15194b9" +dependencies = [ + "autocfg", +] + [[package]] name = "config" version = "0.10.1" @@ -2888,6 +2897,7 @@ dependencies = [ "actix-rt", "actix-web", "chrono", + "claim", "config", "lazy_static", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 044e166..6fbf417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,4 @@ unicode-segmentation = "1.7.1" [dev-dependencies] reqwest = { version = "0.10.7", features = ["json"] } lazy_static = "1.4.0" +claim = "0.4.0" diff --git a/src/domain.rs b/src/domain.rs index 5961b46..cabb0b2 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -5,6 +5,7 @@ pub struct NewSubscriber { pub name: SubscriberName, } +#[derive(Debug)] pub struct SubscriberName(String); impl SubscriberName { @@ -36,7 +37,7 @@ impl SubscriberName { > 0; if is_empty_or_whitespace || is_too_long || contains_forbidden_characters { - panic!(format!("{} is not a valid subscriber name.", s)) + Err(format!("{} is not a valid subscriber name.", s)) } else { Ok(Self(s)) } @@ -48,3 +49,47 @@ impl AsRef for SubscriberName { &self.0 } } + +#[cfg(test)] +mod tests { + use crate::domain::SubscriberName; + use claim::{assert_err, assert_ok}; + + #[test] + fn a_256_grapheme_long_name_is_valid() { + let name = "a̐".repeat(256); + assert_ok!(SubscriberName::parse(name)); + } + + #[test] + fn a_name_longer_than_256_graphemes_is_rejected() { + let name = "a".repeat(257); + assert_err!(SubscriberName::parse(name)); + } + + #[test] + fn whitespace_only_names_are_rejected() { + let name = " ".to_string(); + assert_err!(SubscriberName::parse(name)); + } + + #[test] + fn empty_string_is_rejected() { + let name = "".to_string(); + assert_err!(SubscriberName::parse(name)); + } + + #[test] + fn names_containing_an_invalid_characters_are_rejected() { + for name in vec!['/', '(', ')', '"', '<', '>', '\\', '{', '}'] { + let name = name.to_string(); + assert_err!(SubscriberName::parse(name)); + } + } + + #[test] + fn a_valid_name_is_parsed_successfully() { + let name = "Ursula Le Guin".to_string(); + assert_ok!(SubscriberName::parse(name)); + } +} diff --git a/src/routes/subscriptions.rs b/src/routes/subscriptions.rs index 6e8715c..7e55663 100644 --- a/src/routes/subscriptions.rs +++ b/src/routes/subscriptions.rs @@ -22,9 +22,11 @@ pub async fn subscribe( form: web::Form, pool: web::Data, ) -> Result { + let name = + SubscriberName::parse(form.0.name).map_err(|_| HttpResponse::BadRequest().finish())?; let new_subscriber = NewSubscriber { email: form.0.email, - name: SubscriberName::parse(form.0.name).expect("Name validation failed."), + name, }; insert_subscriber(&pool, &new_subscriber) .await From 6db241eef603e44fb99606db7b1d1a484bd3254b Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Fri, 11 Dec 2020 21:35:16 +0000 Subject: [PATCH 10/10] Fix integration test. --- tests/health_check.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/health_check.rs b/tests/health_check.rs index b3f1bd0..b72efb1 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -143,7 +143,7 @@ async fn subscribe_returns_a_400_when_data_is_missing() { } #[actix_rt::test] -async fn subscribe_returns_a_200_when_fields_are_present_but_empty() { +async fn subscribe_returns_a_400_when_fields_are_present_but_invalid() { // Arrange let app = spawn_app().await; let client = reqwest::Client::new(); @@ -165,9 +165,9 @@ async fn subscribe_returns_a_200_when_fields_are_present_but_empty() { // Assert assert_eq!( - 200, + 400, response.status().as_u16(), - "The API did not return a 200 OK when the payload was {}.", + "The API did not return a 400 Bad Request when the payload was {}.", description ); }