diff --git a/docs-site/content/docs/getting-started/guide.md b/docs-site/content/docs/getting-started/guide.md index e2b09678c..e6c7af476 100644 --- a/docs-site/content/docs/getting-started/guide.md +++ b/docs-site/content/docs/getting-started/guide.md @@ -244,9 +244,7 @@ impl Hooks for App { fn routes() -> AppRoutes { AppRoutes::with_default_routes() .add_route(controllers::guide::routes()) - .add_route(controllers::notes::routes()) .add_route(controllers::auth::routes()) - .add_route(controllers::user::routes()) .add_route(controllers::home::routes()) // <--- add this } ``` @@ -277,8 +275,6 @@ $ cargo loco routes [POST] /api/auth/reset [POST] /api/auth/verify [GET] /home/hello <---- this is our new route! -[GET] /api/notes -[POST] /api/notes .. .. $ @@ -388,12 +384,10 @@ src/models/ ├── _entities │   ├── articles.rs <-- sync'd from db schema, do not edit │   ├── mod.rs -│   ├── notes.rs │   ├── prelude.rs │   └── users.rs ├── articles.rs <-- generated for you, your logic goes here. ├── mod.rs -├── notes.rs └── users.rs ``` diff --git a/docs-site/content/docs/the-app/controller.md b/docs-site/content/docs/the-app/controller.md index d2d70d6c0..cbf35b9ea 100644 --- a/docs-site/content/docs/the-app/controller.md +++ b/docs-site/content/docs/the-app/controller.md @@ -47,12 +47,7 @@ $ cargo loco routes [POST] /auth/register [POST] /auth/reset [POST] /auth/verify -[GET] /notes/ -[POST] /notes/ -[GET] /notes/:id -[DELETE] /notes/:id -[POST] /notes/:id -[GET] /user/current +[GET] /auth/current ``` This command will provide you with a comprehensive overview of the controllers currently registered in your system. @@ -768,6 +763,8 @@ impl Hooks for App { In many scenarios, when querying data and returning responses to users, pagination is crucial. In `Loco`, we provide a straightforward method to paginate your data and maintain a consistent pagination response schema for your API responses. +We assume you have a `notes` entity and/or scaffold (replace this with any entity you like). + ## Using pagination ```rust diff --git a/docs-site/content/docs/the-app/models.md b/docs-site/content/docs/the-app/models.md index a9a4cee8e..60204e333 100644 --- a/docs-site/content/docs/the-app/models.md +++ b/docs-site/content/docs/the-app/models.md @@ -393,7 +393,6 @@ $ cargo loco generate model --link users_votes user:references movie:references .. .. Writing src/models/_entities/movies.rs -Writing src/models/_entities/notes.rs Writing src/models/_entities/users.rs Writing src/models/_entities/mod.rs Writing src/models/_entities/prelude.rs @@ -736,7 +735,6 @@ impl Hooks for App { //... async fn truncate(db: &DatabaseConnection) -> Result<()> { // truncate_table(db, users::Entity).await?; - // truncate_table(db, notes::Entity).await?; Ok(()) } diff --git a/docs-site/content/docs/the-app/views.md b/docs-site/content/docs/the-app/views.md index 8434b02cb..919d34354 100644 --- a/docs-site/content/docs/the-app/views.md +++ b/docs-site/content/docs/the-app/views.md @@ -179,9 +179,7 @@ impl Hooks for App { fn routes(_ctx: &AppContext) -> AppRoutes { AppRoutes::with_default_routes() - .add_route(controllers::notes::routes()) .add_route(controllers::auth::routes()) - .add_route(controllers::user::routes()) // include your controller's routes here .add_route(controllers::dashboard::routes()) } @@ -198,12 +196,7 @@ $ cargo loco routes [POST] /api/auth/register [POST] /api/auth/reset [POST] /api/auth/verify -[GET] /api/notes -[POST] /api/notes -[GET] /api/notes/:id -[DELETE] /api/notes/:id -[POST] /api/notes/:id -[GET] /api/user/current +[GET] /api/auth/current [GET] /home <-- the corresponding URL for our new view ``` diff --git a/starters/lightweight-service/.devcontainer/.env b/starters/lightweight-service/.devcontainer/.env deleted file mode 100644 index e69de29bb..000000000 diff --git a/starters/lightweight-service/.devcontainer/Dockerfile b/starters/lightweight-service/.devcontainer/Dockerfile deleted file mode 100644 index 70bb85887..000000000 --- a/starters/lightweight-service/.devcontainer/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/rust:1 - -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends postgresql-client \ - && cargo install cargo-insta \ - && chown -R vscode /usr/local/cargo - -COPY .env /.env diff --git a/starters/lightweight-service/.devcontainer/compose.yaml b/starters/lightweight-service/.devcontainer/compose.yaml deleted file mode 100644 index 78d4d1164..000000000 --- a/starters/lightweight-service/.devcontainer/compose.yaml +++ /dev/null @@ -1,10 +0,0 @@ -services: - app: - build: - context: . - dockerfile: Dockerfile - command: sleep infinity - volumes: - - ../..:/workspaces:cached - env_file: - - .env diff --git a/starters/lightweight-service/.devcontainer/devcontainer.json b/starters/lightweight-service/.devcontainer/devcontainer.json deleted file mode 100644 index b2d83c254..000000000 --- a/starters/lightweight-service/.devcontainer/devcontainer.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Loco", - "dockerComposeFile": "compose.yaml", - "service": "app", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", - "forwardPorts": [ - 5150 - ] -} \ No newline at end of file diff --git a/starters/lightweight-service/src/app.rs b/starters/lightweight-service/src/app.rs index 2e7fff178..23ec92bed 100644 --- a/starters/lightweight-service/src/app.rs +++ b/starters/lightweight-service/src/app.rs @@ -35,8 +35,7 @@ impl Hooks for App { } fn routes(_ctx: &AppContext) -> AppRoutes { - AppRoutes::empty() - .prefix("/api") + AppRoutes::empty() // controller routes below .add_route(controllers::home::routes()) } diff --git a/starters/lightweight-service/src/controllers/home.rs b/starters/lightweight-service/src/controllers/home.rs index d3330ebb5..0d6430f60 100644 --- a/starters/lightweight-service/src/controllers/home.rs +++ b/starters/lightweight-service/src/controllers/home.rs @@ -9,5 +9,5 @@ async fn current() -> Result { } pub fn routes() -> Routes { - Routes::new().add("/", get(current)) + Routes::new().prefix("/api").add("/", get(current)) } diff --git a/starters/rest-api/.devcontainer/.env b/starters/rest-api/.devcontainer/.env deleted file mode 100644 index 1a3ca5b3c..000000000 --- a/starters/rest-api/.devcontainer/.env +++ /dev/null @@ -1,6 +0,0 @@ -POSTGRES_DB=loco_app -POSTGRES_USER=loco -POSTGRES_PASSWORD=loco -DATABASE_URL=postgres://loco:loco@db:5432/loco_app -REDIS_URL=redis://redis:6379 -MAILER_HOST=mailer \ No newline at end of file diff --git a/starters/rest-api/.devcontainer/Dockerfile b/starters/rest-api/.devcontainer/Dockerfile deleted file mode 100644 index 49616a5f8..000000000 --- a/starters/rest-api/.devcontainer/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/rust:1 - -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends postgresql-client \ - && cargo install sea-orm-cli cargo-insta \ - && chown -R vscode /usr/local/cargo - -COPY .env /.env diff --git a/starters/rest-api/.devcontainer/compose.yaml b/starters/rest-api/.devcontainer/compose.yaml deleted file mode 100644 index e461417d2..000000000 --- a/starters/rest-api/.devcontainer/compose.yaml +++ /dev/null @@ -1,49 +0,0 @@ -services: - app: - build: - context: . - dockerfile: Dockerfile - command: sleep infinity - networks: - - db - - redis - - mailer - volumes: - - ../..:/workspaces:cached - env_file: - - .env - db: - image: postgres:15.3-alpine - restart: unless-stopped - ports: - - 5432:5432 - networks: - - db - volumes: - - postgres-data:/var/lib/postgresql/data - env_file: - - .env - redis: - image: redis:latest - restart: unless-stopped - ports: - - 6379:6379 - networks: - - redis - mailer: - image: mailtutan/mailtutan:latest - restart: unless-stopped - ports: - - 1080:1080 - - 1025:1025 - networks: - - mailer - -volumes: - postgres-data: - - -networks: - db: - redis: - mailer: diff --git a/starters/rest-api/.devcontainer/devcontainer.json b/starters/rest-api/.devcontainer/devcontainer.json deleted file mode 100644 index b2d83c254..000000000 --- a/starters/rest-api/.devcontainer/devcontainer.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Loco", - "dockerComposeFile": "compose.yaml", - "service": "app", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", - "forwardPorts": [ - 5150 - ] -} \ No newline at end of file diff --git a/starters/rest-api/migration/src/lib.rs b/starters/rest-api/migration/src/lib.rs index cdf5ebc7b..990d92572 100644 --- a/starters/rest-api/migration/src/lib.rs +++ b/starters/rest-api/migration/src/lib.rs @@ -3,17 +3,12 @@ pub use sea_orm_migration::prelude::*; mod m20220101_000001_users; -mod m20231103_114510_notes; pub struct Migrator; #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec> { - vec![ - // inject-below (do not remove this comment) - Box::new(m20220101_000001_users::Migration), - Box::new(m20231103_114510_notes::Migration), - ] + vec![Box::new(m20220101_000001_users::Migration)] } } diff --git a/starters/rest-api/migration/src/m20231103_114510_notes.rs b/starters/rest-api/migration/src/m20231103_114510_notes.rs deleted file mode 100644 index d0d8a5a0f..000000000 --- a/starters/rest-api/migration/src/m20231103_114510_notes.rs +++ /dev/null @@ -1,34 +0,0 @@ -use loco_rs::schema::table_auto_tz; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - table_auto_tz(Notes::Table) - .col(pk_auto(Notes::Id)) - .col(string_null(Notes::Title)) - .col(string_null(Notes::Content)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Notes::Table).to_owned()) - .await - } -} - -#[derive(DeriveIden)] -enum Notes { - Table, - Id, - Title, - Content, -} diff --git a/starters/rest-api/src/app.rs b/starters/rest-api/src/app.rs index c2f802bcc..3efccf21d 100644 --- a/starters/rest-api/src/app.rs +++ b/starters/rest-api/src/app.rs @@ -14,12 +14,7 @@ use loco_rs::{ use migration::Migrator; use sea_orm::DatabaseConnection; -use crate::{ - controllers, - models::_entities::{notes, users}, - tasks, - workers::downloader::DownloadWorker, -}; +use crate::{controllers, models::_entities::users, tasks, workers::downloader::DownloadWorker}; pub struct App; #[async_trait] @@ -43,11 +38,8 @@ impl Hooks for App { } fn routes(_ctx: &AppContext) -> AppRoutes { - AppRoutes::with_default_routes() - .prefix("/api") - .add_route(controllers::notes::routes()) + AppRoutes::with_default_routes() // controller routes below .add_route(controllers::auth::routes()) - .add_route(controllers::user::routes()) } async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { @@ -61,13 +53,11 @@ impl Hooks for App { async fn truncate(db: &DatabaseConnection) -> Result<()> { truncate_table(db, users::Entity).await?; - truncate_table(db, notes::Entity).await?; Ok(()) } async fn seed(db: &DatabaseConnection, base: &Path) -> Result<()> { db::seed::(db, &base.join("users.yaml").display().to_string()).await?; - db::seed::(db, &base.join("notes.yaml").display().to_string()).await?; Ok(()) } } diff --git a/starters/rest-api/src/controllers/auth.rs b/starters/rest-api/src/controllers/auth.rs index dba78a3e9..27e3de71e 100644 --- a/starters/rest-api/src/controllers/auth.rs +++ b/starters/rest-api/src/controllers/auth.rs @@ -8,7 +8,7 @@ use crate::{ _entities::users, users::{LoginParams, RegisterParams}, }, - views::auth::LoginResponse, + views::auth::{CurrentResponse, LoginResponse}, }; #[derive(Debug, Deserialize, Serialize)] pub struct VerifyParams { @@ -139,12 +139,19 @@ async fn login(State(ctx): State, Json(params): Json) - format::json(LoginResponse::new(&user, &token)) } +#[debug_handler] +async fn current(auth: auth::JWT, State(ctx): State) -> Result { + let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?; + format::json(CurrentResponse::new(&user)) +} + pub fn routes() -> Routes { Routes::new() - .prefix("auth") + .prefix("/api/auth") .add("/register", post(register)) .add("/verify", post(verify)) .add("/login", post(login)) .add("/forgot", post(forgot)) .add("/reset", post(reset)) + .add("/current", get(current)) } diff --git a/starters/rest-api/src/controllers/mod.rs b/starters/rest-api/src/controllers/mod.rs index 0a3d0b5fb..0e4a05d59 100644 --- a/starters/rest-api/src/controllers/mod.rs +++ b/starters/rest-api/src/controllers/mod.rs @@ -1,3 +1 @@ pub mod auth; -pub mod notes; -pub mod user; diff --git a/starters/rest-api/src/controllers/notes.rs b/starters/rest-api/src/controllers/notes.rs deleted file mode 100644 index f378fb4d8..000000000 --- a/starters/rest-api/src/controllers/notes.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::unnecessary_struct_initialization)] -#![allow(clippy::unused_async)] -use axum::debug_handler; -use loco_rs::prelude::*; -use serde::{Deserialize, Serialize}; - -use crate::models::_entities::notes::{ActiveModel, Entity, Model}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Params { - pub title: Option, - pub content: Option, -} - -impl Params { - fn update(&self, item: &mut ActiveModel) { - item.title = Set(self.title.clone()); - item.content = Set(self.content.clone()); - } -} - -async fn load_item(ctx: &AppContext, id: i32) -> Result { - let item = Entity::find_by_id(id).one(&ctx.db).await?; - item.ok_or_else(|| Error::NotFound) -} - -#[debug_handler] -pub async fn list(State(ctx): State) -> Result { - format::json(Entity::find().all(&ctx.db).await?) -} - -#[debug_handler] -pub async fn add(State(ctx): State, Json(params): Json) -> Result { - let mut item = ActiveModel { - ..Default::default() - }; - params.update(&mut item); - let item = item.insert(&ctx.db).await?; - format::json(item) -} - -#[debug_handler] -pub async fn update( - Path(id): Path, - State(ctx): State, - Json(params): Json, -) -> Result { - let item = load_item(&ctx, id).await?; - let mut item = item.into_active_model(); - params.update(&mut item); - let item = item.update(&ctx.db).await?; - format::json(item) -} - -#[debug_handler] -pub async fn remove(Path(id): Path, State(ctx): State) -> Result { - load_item(&ctx, id).await?.delete(&ctx.db).await?; - format::empty() -} - -#[debug_handler] -pub async fn get_one(Path(id): Path, State(ctx): State) -> Result { - format::json(load_item(&ctx, id).await?) -} - -pub fn routes() -> Routes { - Routes::new() - .prefix("notes") - .add("/", get(list)) - .add("/", post(add)) - .add("/:id", get(get_one)) - .add("/:id", delete(remove)) - .add("/:id", post(update)) -} diff --git a/starters/rest-api/src/controllers/user.rs b/starters/rest-api/src/controllers/user.rs deleted file mode 100644 index 1f432ae9e..000000000 --- a/starters/rest-api/src/controllers/user.rs +++ /dev/null @@ -1,14 +0,0 @@ -use axum::debug_handler; -use loco_rs::prelude::*; - -use crate::{models::_entities::users, views::user::CurrentResponse}; - -#[debug_handler] -async fn current(auth: auth::JWT, State(ctx): State) -> Result { - let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?; - format::json(CurrentResponse::new(&user)) -} - -pub fn routes() -> Routes { - Routes::new().prefix("user").add("/current", get(current)) -} diff --git a/starters/rest-api/src/fixtures/notes.yaml b/starters/rest-api/src/fixtures/notes.yaml deleted file mode 100644 index 0e66c9531..000000000 --- a/starters/rest-api/src/fixtures/notes.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -- id: 1 - title: Loco note 1 - content: Loco note 1 content - created_at: "2023-11-12T12:34:56.789Z" - updated_at: "2023-11-12T12:34:56.789Z" -- id: 2 - title: Loco note 2 - content: Loco note 2 content - created_at: "2023-11-12T12:34:56.789Z" - updated_at: "2023-11-12T12:34:56.789Z" diff --git a/starters/rest-api/src/models/_entities/mod.rs b/starters/rest-api/src/models/_entities/mod.rs index c04afbb65..095dade0f 100644 --- a/starters/rest-api/src/models/_entities/mod.rs +++ b/starters/rest-api/src/models/_entities/mod.rs @@ -2,5 +2,4 @@ pub mod prelude; -pub mod notes; pub mod users; diff --git a/starters/rest-api/src/models/_entities/notes.rs b/starters/rest-api/src/models/_entities/notes.rs deleted file mode 100644 index a80335389..000000000 --- a/starters/rest-api/src/models/_entities/notes.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 - -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] -#[sea_orm(table_name = "notes")] -pub struct Model { - pub created_at: DateTimeWithTimeZone, - pub updated_at: DateTimeWithTimeZone, - #[sea_orm(primary_key)] - pub id: i32, - pub title: Option, - pub content: Option, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} diff --git a/starters/rest-api/src/models/_entities/prelude.rs b/starters/rest-api/src/models/_entities/prelude.rs index 4d1101962..4036adeec 100644 --- a/starters/rest-api/src/models/_entities/prelude.rs +++ b/starters/rest-api/src/models/_entities/prelude.rs @@ -1,4 +1,3 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 -pub use super::notes::Entity as Notes; pub use super::users::Entity as Users; diff --git a/starters/rest-api/src/models/mod.rs b/starters/rest-api/src/models/mod.rs index 917969b1c..48da463b6 100644 --- a/starters/rest-api/src/models/mod.rs +++ b/starters/rest-api/src/models/mod.rs @@ -1,3 +1,2 @@ pub mod _entities; -pub mod notes; pub mod users; diff --git a/starters/rest-api/src/models/notes.rs b/starters/rest-api/src/models/notes.rs deleted file mode 100644 index 110259823..000000000 --- a/starters/rest-api/src/models/notes.rs +++ /dev/null @@ -1,7 +0,0 @@ -use sea_orm::entity::prelude::*; - -use super::_entities::notes::ActiveModel; - -impl ActiveModelBehavior for ActiveModel { - // extend activemodel below (keep comment for generators) -} diff --git a/starters/rest-api/src/views/auth.rs b/starters/rest-api/src/views/auth.rs index 2240a5087..3d2d74fdd 100644 --- a/starters/rest-api/src/views/auth.rs +++ b/starters/rest-api/src/views/auth.rs @@ -21,3 +21,21 @@ impl LoginResponse { } } } + +#[derive(Debug, Deserialize, Serialize)] +pub struct CurrentResponse { + pub pid: String, + pub name: String, + pub email: String, +} + +impl CurrentResponse { + #[must_use] + pub fn new(user: &users::Model) -> Self { + Self { + pid: user.pid.to_string(), + name: user.name.clone(), + email: user.email.clone(), + } + } +} diff --git a/starters/rest-api/src/views/mod.rs b/starters/rest-api/src/views/mod.rs index f9bae3db2..0e4a05d59 100644 --- a/starters/rest-api/src/views/mod.rs +++ b/starters/rest-api/src/views/mod.rs @@ -1,2 +1 @@ pub mod auth; -pub mod user; diff --git a/starters/rest-api/src/views/user.rs b/starters/rest-api/src/views/user.rs deleted file mode 100644 index 9d830410f..000000000 --- a/starters/rest-api/src/views/user.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::models::_entities::users; - -#[derive(Debug, Deserialize, Serialize)] -pub struct CurrentResponse { - pub pid: String, - pub name: String, - pub email: String, -} - -impl CurrentResponse { - #[must_use] - pub fn new(user: &users::Model) -> Self { - Self { - pid: user.pid.to_string(), - name: user.name.clone(), - email: user.email.clone(), - } - } -} diff --git a/starters/rest-api/tests/requests/auth.rs b/starters/rest-api/tests/requests/auth.rs index 5a996c899..0ebefd2d4 100644 --- a/starters/rest-api/tests/requests/auth.rs +++ b/starters/rest-api/tests/requests/auth.rs @@ -193,3 +193,26 @@ async fn can_reset_password() { }) .await; } + +#[tokio::test] +#[serial] +async fn can_get_current_user() { + configure_insta!(); + + testing::request::(|request, ctx| async move { + let user = prepare_data::init_user_login(&request, &ctx).await; + + let (auth_key, auth_value) = prepare_data::auth_header(&user.token); + let response = request + .get("/api/user/current") + .add_header(auth_key, auth_value) + .await; + + with_settings!({ + filters => testing::cleanup_user_model() + }, { + assert_debug_snapshot!((response.status_code(), response.text())); + }); + }) + .await; +} diff --git a/starters/rest-api/tests/requests/mod.rs b/starters/rest-api/tests/requests/mod.rs index 81ed68f96..887b7ce0b 100644 --- a/starters/rest-api/tests/requests/mod.rs +++ b/starters/rest-api/tests/requests/mod.rs @@ -1,4 +1,2 @@ mod auth; -mod notes; mod prepare_data; -mod user; diff --git a/starters/rest-api/tests/requests/notes.rs b/starters/rest-api/tests/requests/notes.rs deleted file mode 100644 index 7f74749f8..000000000 --- a/starters/rest-api/tests/requests/notes.rs +++ /dev/null @@ -1,123 +0,0 @@ -use insta::{assert_debug_snapshot, with_settings}; -use loco_rs::testing; -use loco_starter_template::{app::App, models::_entities::notes::Entity}; -use sea_orm::entity::prelude::*; -use serial_test::serial; - -// TODO: see how to dedup / extract this to app-local test utils -// not to framework, because that would require a runtime dep on insta -macro_rules! configure_insta { - ($($expr:expr),*) => { - let mut settings = insta::Settings::clone_current(); - settings.set_prepend_module_to_snapshot(false); - settings.set_snapshot_suffix("notes_request"); - let _guard = settings.bind_to_scope(); - }; -} - -#[tokio::test] -#[serial] -async fn can_get_notes() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let notes = request.get("/api/notes").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (notes.status_code(), notes.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_add_note() { - configure_insta!(); - - testing::request::(|request, _ctx| async move { - let payload = serde_json::json!({ - "title": "loco", - "content": "loco note test", - }); - - let add_note_request = request.post("/api/notes").json(&payload).await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (add_note_request.status_code(), add_note_request.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_get_note() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let add_note_request = request.get("/api/notes/1").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (add_note_request.status_code(), add_note_request.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_delete_note() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let count_before_delete = Entity::find().all(&ctx.db).await.unwrap().len(); - let delete_note_request = request.delete("/api/notes/1").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (delete_note_request.status_code(), delete_note_request.text()) - ); - }); - - let count_after_delete = Entity::find().all(&ctx.db).await.unwrap().len(); - assert_eq!(count_after_delete, count_before_delete - 1); - }) - .await; -} diff --git a/starters/rest-api/tests/requests/snapshots/can_add_note@notes_request.snap b/starters/rest-api/tests/requests/snapshots/can_add_note@notes_request.snap deleted file mode 100644 index f8457d7a4..000000000 --- a/starters/rest-api/tests/requests/snapshots/can_add_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(add_note_request.status_code(), add_note_request.text())" ---- -( - 200, - "{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"loco\",\"content\":\"loco note test\"}", -) diff --git a/starters/rest-api/tests/requests/snapshots/can_delete_note@notes_request.snap b/starters/rest-api/tests/requests/snapshots/can_delete_note@notes_request.snap deleted file mode 100644 index 3481fc36d..000000000 --- a/starters/rest-api/tests/requests/snapshots/can_delete_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(delete_note_request.status_code(), delete_note_request.text())" ---- -( - 200, - "", -) diff --git a/starters/rest-api/tests/requests/snapshots/can_get_current_user@auth_request.snap b/starters/rest-api/tests/requests/snapshots/can_get_current_user@auth_request.snap new file mode 100644 index 000000000..7b9b93a87 --- /dev/null +++ b/starters/rest-api/tests/requests/snapshots/can_get_current_user@auth_request.snap @@ -0,0 +1,8 @@ +--- +source: tests/requests/auth.rs +expression: "(response.status_code(), response.text())" +--- +( + 200, + "\n\n\n\n \n \n Welcome to Loco!\n \n \n\n\n\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n

Welcome to Loco!

\n

It looks like you've just started your Loco server, and this is the fallback page displayed when a route doesn't exist.

\n \n
\n
\n

Remove this Fallback Page

\n

To remove this fallback page, adjust the configuration in your config/development.yaml file. Look for the fallback: setting and disable or customize it.

\n
\n
\n

Scaffold Your Application

\n

Use the Loco CLI to scaffold your application:

\n
cargo loco generate scaffold movie title:string
\n

This creates models, controllers, and views.

\n
\n
\n\n
\n

Need More Help?

\n

If you need further assistance, check out the Loco documentation or post on our discussions forum.

\n
\n
\n
\n\n\n\n\n", +) diff --git a/starters/rest-api/tests/requests/snapshots/can_get_note@notes_request.snap b/starters/rest-api/tests/requests/snapshots/can_get_note@notes_request.snap deleted file mode 100644 index 8af1604c9..000000000 --- a/starters/rest-api/tests/requests/snapshots/can_get_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(add_note_request.status_code(), add_note_request.text())" ---- -( - 200, - "{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"Loco note 1 content\"}", -) diff --git a/starters/rest-api/tests/requests/snapshots/can_get_notes@notes_request.snap b/starters/rest-api/tests/requests/snapshots/can_get_notes@notes_request.snap deleted file mode 100644 index 014b75c03..000000000 --- a/starters/rest-api/tests/requests/snapshots/can_get_notes@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(notes.status_code(), notes.text())" ---- -( - 200, - "[{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"Loco note 1 content\"},{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 2\",\"content\":\"Loco note 2 content\"}]", -) diff --git a/starters/rest-api/tests/requests/user.rs b/starters/rest-api/tests/requests/user.rs deleted file mode 100644 index 92705079c..000000000 --- a/starters/rest-api/tests/requests/user.rs +++ /dev/null @@ -1,40 +0,0 @@ -use insta::{assert_debug_snapshot, with_settings}; -use loco_rs::testing; -use loco_starter_template::app::App; -use serial_test::serial; - -use super::prepare_data; - -// TODO: see how to dedup / extract this to app-local test utils -// not to framework, because that would require a runtime dep on insta -macro_rules! configure_insta { - ($($expr:expr),*) => { - let mut settings = insta::Settings::clone_current(); - settings.set_prepend_module_to_snapshot(false); - settings.set_snapshot_suffix("user_request"); - let _guard = settings.bind_to_scope(); - }; -} - -#[tokio::test] -#[serial] -async fn can_get_current_user() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - let user = prepare_data::init_user_login(&request, &ctx).await; - - let (auth_key, auth_value) = prepare_data::auth_header(&user.token); - let response = request - .get("/api/user/current") - .add_header(auth_key, auth_value) - .await; - - with_settings!({ - filters => testing::cleanup_user_model() - }, { - assert_debug_snapshot!((response.status_code(), response.text())); - }); - }) - .await; -} diff --git a/starters/saas/.devcontainer/.env b/starters/saas/.devcontainer/.env deleted file mode 100644 index 1a3ca5b3c..000000000 --- a/starters/saas/.devcontainer/.env +++ /dev/null @@ -1,6 +0,0 @@ -POSTGRES_DB=loco_app -POSTGRES_USER=loco -POSTGRES_PASSWORD=loco -DATABASE_URL=postgres://loco:loco@db:5432/loco_app -REDIS_URL=redis://redis:6379 -MAILER_HOST=mailer \ No newline at end of file diff --git a/starters/saas/.devcontainer/Dockerfile b/starters/saas/.devcontainer/Dockerfile deleted file mode 100644 index 49616a5f8..000000000 --- a/starters/saas/.devcontainer/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/rust:1 - -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends postgresql-client \ - && cargo install sea-orm-cli cargo-insta \ - && chown -R vscode /usr/local/cargo - -COPY .env /.env diff --git a/starters/saas/.devcontainer/compose.yaml b/starters/saas/.devcontainer/compose.yaml deleted file mode 100644 index e461417d2..000000000 --- a/starters/saas/.devcontainer/compose.yaml +++ /dev/null @@ -1,49 +0,0 @@ -services: - app: - build: - context: . - dockerfile: Dockerfile - command: sleep infinity - networks: - - db - - redis - - mailer - volumes: - - ../..:/workspaces:cached - env_file: - - .env - db: - image: postgres:15.3-alpine - restart: unless-stopped - ports: - - 5432:5432 - networks: - - db - volumes: - - postgres-data:/var/lib/postgresql/data - env_file: - - .env - redis: - image: redis:latest - restart: unless-stopped - ports: - - 6379:6379 - networks: - - redis - mailer: - image: mailtutan/mailtutan:latest - restart: unless-stopped - ports: - - 1080:1080 - - 1025:1025 - networks: - - mailer - -volumes: - postgres-data: - - -networks: - db: - redis: - mailer: diff --git a/starters/saas/.devcontainer/devcontainer.json b/starters/saas/.devcontainer/devcontainer.json deleted file mode 100644 index b2d83c254..000000000 --- a/starters/saas/.devcontainer/devcontainer.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "Loco", - "dockerComposeFile": "compose.yaml", - "service": "app", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", - "forwardPorts": [ - 5150 - ] -} \ No newline at end of file diff --git a/starters/saas/migration/src/lib.rs b/starters/saas/migration/src/lib.rs index cdf5ebc7b..990d92572 100644 --- a/starters/saas/migration/src/lib.rs +++ b/starters/saas/migration/src/lib.rs @@ -3,17 +3,12 @@ pub use sea_orm_migration::prelude::*; mod m20220101_000001_users; -mod m20231103_114510_notes; pub struct Migrator; #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec> { - vec![ - // inject-below (do not remove this comment) - Box::new(m20220101_000001_users::Migration), - Box::new(m20231103_114510_notes::Migration), - ] + vec![Box::new(m20220101_000001_users::Migration)] } } diff --git a/starters/saas/migration/src/m20231103_114510_notes.rs b/starters/saas/migration/src/m20231103_114510_notes.rs deleted file mode 100644 index d0d8a5a0f..000000000 --- a/starters/saas/migration/src/m20231103_114510_notes.rs +++ /dev/null @@ -1,34 +0,0 @@ -use loco_rs::schema::table_auto_tz; -use sea_orm_migration::{prelude::*, schema::*}; - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - table_auto_tz(Notes::Table) - .col(pk_auto(Notes::Id)) - .col(string_null(Notes::Title)) - .col(string_null(Notes::Content)) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Notes::Table).to_owned()) - .await - } -} - -#[derive(DeriveIden)] -enum Notes { - Table, - Id, - Title, - Content, -} diff --git a/starters/saas/src/app.rs b/starters/saas/src/app.rs index 62d3c9466..f635b3787 100644 --- a/starters/saas/src/app.rs +++ b/starters/saas/src/app.rs @@ -15,10 +15,7 @@ use migration::Migrator; use sea_orm::DatabaseConnection; use crate::{ - controllers, initializers, - models::_entities::{notes, users}, - tasks, - workers::downloader::DownloadWorker, + controllers, initializers, models::_entities::users, tasks, workers::downloader::DownloadWorker, }; pub struct App; @@ -49,10 +46,8 @@ impl Hooks for App { } fn routes(_ctx: &AppContext) -> AppRoutes { - AppRoutes::with_default_routes() - .add_route(controllers::notes::routes()) + AppRoutes::with_default_routes() // controller routes below .add_route(controllers::auth::routes()) - .add_route(controllers::user::routes()) } async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> { @@ -66,13 +61,11 @@ impl Hooks for App { async fn truncate(db: &DatabaseConnection) -> Result<()> { truncate_table(db, users::Entity).await?; - truncate_table(db, notes::Entity).await?; Ok(()) } async fn seed(db: &DatabaseConnection, base: &Path) -> Result<()> { db::seed::(db, &base.join("users.yaml").display().to_string()).await?; - db::seed::(db, &base.join("notes.yaml").display().to_string()).await?; Ok(()) } } diff --git a/starters/saas/src/controllers/auth.rs b/starters/saas/src/controllers/auth.rs index 9978d9d21..27e3de71e 100644 --- a/starters/saas/src/controllers/auth.rs +++ b/starters/saas/src/controllers/auth.rs @@ -8,7 +8,7 @@ use crate::{ _entities::users, users::{LoginParams, RegisterParams}, }, - views::auth::LoginResponse, + views::auth::{CurrentResponse, LoginResponse}, }; #[derive(Debug, Deserialize, Serialize)] pub struct VerifyParams { @@ -139,12 +139,19 @@ async fn login(State(ctx): State, Json(params): Json) - format::json(LoginResponse::new(&user, &token)) } +#[debug_handler] +async fn current(auth: auth::JWT, State(ctx): State) -> Result { + let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?; + format::json(CurrentResponse::new(&user)) +} + pub fn routes() -> Routes { Routes::new() - .prefix("api/auth") + .prefix("/api/auth") .add("/register", post(register)) .add("/verify", post(verify)) .add("/login", post(login)) .add("/forgot", post(forgot)) .add("/reset", post(reset)) + .add("/current", get(current)) } diff --git a/starters/saas/src/controllers/mod.rs b/starters/saas/src/controllers/mod.rs index 0a3d0b5fb..0e4a05d59 100644 --- a/starters/saas/src/controllers/mod.rs +++ b/starters/saas/src/controllers/mod.rs @@ -1,3 +1 @@ pub mod auth; -pub mod notes; -pub mod user; diff --git a/starters/saas/src/controllers/notes.rs b/starters/saas/src/controllers/notes.rs deleted file mode 100644 index c95aa3e39..000000000 --- a/starters/saas/src/controllers/notes.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::unnecessary_struct_initialization)] -#![allow(clippy::unused_async)] -use axum::debug_handler; -use loco_rs::prelude::*; -use serde::{Deserialize, Serialize}; - -use crate::models::_entities::notes::{ActiveModel, Entity, Model}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Params { - pub title: Option, - pub content: Option, -} - -impl Params { - fn update(&self, item: &mut ActiveModel) { - item.title = Set(self.title.clone()); - item.content = Set(self.content.clone()); - } -} - -async fn load_item(ctx: &AppContext, id: i32) -> Result { - let item = Entity::find_by_id(id).one(&ctx.db).await?; - item.ok_or_else(|| Error::NotFound) -} - -#[debug_handler] -pub async fn list(State(ctx): State) -> Result { - format::json(Entity::find().all(&ctx.db).await?) -} - -#[debug_handler] -pub async fn add(State(ctx): State, Json(params): Json) -> Result { - let mut item = ActiveModel { - ..Default::default() - }; - params.update(&mut item); - let item = item.insert(&ctx.db).await?; - format::json(item) -} - -#[debug_handler] -pub async fn update( - Path(id): Path, - State(ctx): State, - Json(params): Json, -) -> Result { - let item = load_item(&ctx, id).await?; - let mut item = item.into_active_model(); - params.update(&mut item); - let item = item.update(&ctx.db).await?; - format::json(item) -} - -#[debug_handler] -pub async fn remove(Path(id): Path, State(ctx): State) -> Result { - load_item(&ctx, id).await?.delete(&ctx.db).await?; - format::empty() -} - -#[debug_handler] -pub async fn get_one(Path(id): Path, State(ctx): State) -> Result { - format::json(load_item(&ctx, id).await?) -} - -pub fn routes() -> Routes { - Routes::new() - .prefix("api/notes") - .add("/", get(list)) - .add("/", post(add)) - .add("/:id", get(get_one)) - .add("/:id", delete(remove)) - .add("/:id", post(update)) -} diff --git a/starters/saas/src/controllers/user.rs b/starters/saas/src/controllers/user.rs deleted file mode 100644 index a7c0af334..000000000 --- a/starters/saas/src/controllers/user.rs +++ /dev/null @@ -1,16 +0,0 @@ -use axum::debug_handler; -use loco_rs::prelude::*; - -use crate::{models::_entities::users, views::user::CurrentResponse}; - -#[debug_handler] -async fn current(auth: auth::JWT, State(ctx): State) -> Result { - let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?; - format::json(CurrentResponse::new(&user)) -} - -pub fn routes() -> Routes { - Routes::new() - .prefix("api/user") - .add("/current", get(current)) -} diff --git a/starters/saas/src/fixtures/notes.yaml b/starters/saas/src/fixtures/notes.yaml deleted file mode 100644 index 0e66c9531..000000000 --- a/starters/saas/src/fixtures/notes.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -- id: 1 - title: Loco note 1 - content: Loco note 1 content - created_at: "2023-11-12T12:34:56.789Z" - updated_at: "2023-11-12T12:34:56.789Z" -- id: 2 - title: Loco note 2 - content: Loco note 2 content - created_at: "2023-11-12T12:34:56.789Z" - updated_at: "2023-11-12T12:34:56.789Z" diff --git a/starters/saas/src/models/_entities/mod.rs b/starters/saas/src/models/_entities/mod.rs index c04afbb65..095dade0f 100644 --- a/starters/saas/src/models/_entities/mod.rs +++ b/starters/saas/src/models/_entities/mod.rs @@ -2,5 +2,4 @@ pub mod prelude; -pub mod notes; pub mod users; diff --git a/starters/saas/src/models/_entities/notes.rs b/starters/saas/src/models/_entities/notes.rs deleted file mode 100644 index a80335389..000000000 --- a/starters/saas/src/models/_entities/notes.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 - -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] -#[sea_orm(table_name = "notes")] -pub struct Model { - pub created_at: DateTimeWithTimeZone, - pub updated_at: DateTimeWithTimeZone, - #[sea_orm(primary_key)] - pub id: i32, - pub title: Option, - pub content: Option, -} - -#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} diff --git a/starters/saas/src/models/_entities/prelude.rs b/starters/saas/src/models/_entities/prelude.rs index 4d1101962..4036adeec 100644 --- a/starters/saas/src/models/_entities/prelude.rs +++ b/starters/saas/src/models/_entities/prelude.rs @@ -1,4 +1,3 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 1.0.0 -pub use super::notes::Entity as Notes; pub use super::users::Entity as Users; diff --git a/starters/saas/src/models/mod.rs b/starters/saas/src/models/mod.rs index 917969b1c..48da463b6 100644 --- a/starters/saas/src/models/mod.rs +++ b/starters/saas/src/models/mod.rs @@ -1,3 +1,2 @@ pub mod _entities; -pub mod notes; pub mod users; diff --git a/starters/saas/src/models/notes.rs b/starters/saas/src/models/notes.rs deleted file mode 100644 index 110259823..000000000 --- a/starters/saas/src/models/notes.rs +++ /dev/null @@ -1,7 +0,0 @@ -use sea_orm::entity::prelude::*; - -use super::_entities::notes::ActiveModel; - -impl ActiveModelBehavior for ActiveModel { - // extend activemodel below (keep comment for generators) -} diff --git a/starters/saas/src/views/auth.rs b/starters/saas/src/views/auth.rs index 2240a5087..3d2d74fdd 100644 --- a/starters/saas/src/views/auth.rs +++ b/starters/saas/src/views/auth.rs @@ -21,3 +21,21 @@ impl LoginResponse { } } } + +#[derive(Debug, Deserialize, Serialize)] +pub struct CurrentResponse { + pub pid: String, + pub name: String, + pub email: String, +} + +impl CurrentResponse { + #[must_use] + pub fn new(user: &users::Model) -> Self { + Self { + pid: user.pid.to_string(), + name: user.name.clone(), + email: user.email.clone(), + } + } +} diff --git a/starters/saas/src/views/mod.rs b/starters/saas/src/views/mod.rs index f9bae3db2..0e4a05d59 100644 --- a/starters/saas/src/views/mod.rs +++ b/starters/saas/src/views/mod.rs @@ -1,2 +1 @@ pub mod auth; -pub mod user; diff --git a/starters/saas/src/views/user.rs b/starters/saas/src/views/user.rs deleted file mode 100644 index 9d830410f..000000000 --- a/starters/saas/src/views/user.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::models::_entities::users; - -#[derive(Debug, Deserialize, Serialize)] -pub struct CurrentResponse { - pub pid: String, - pub name: String, - pub email: String, -} - -impl CurrentResponse { - #[must_use] - pub fn new(user: &users::Model) -> Self { - Self { - pid: user.pid.to_string(), - name: user.name.clone(), - email: user.email.clone(), - } - } -} diff --git a/starters/saas/tests/requests/auth.rs b/starters/saas/tests/requests/auth.rs index 5a996c899..c5c9ba940 100644 --- a/starters/saas/tests/requests/auth.rs +++ b/starters/saas/tests/requests/auth.rs @@ -193,3 +193,26 @@ async fn can_reset_password() { }) .await; } + +#[tokio::test] +#[serial] +async fn can_get_current_user() { + configure_insta!(); + + testing::request::(|request, ctx| async move { + let user = prepare_data::init_user_login(&request, &ctx).await; + + let (auth_key, auth_value) = prepare_data::auth_header(&user.token); + let response = request + .get("/api/auth/current") + .add_header(auth_key, auth_value) + .await; + + with_settings!({ + filters => testing::cleanup_user_model() + }, { + assert_debug_snapshot!((response.status_code(), response.text())); + }); + }) + .await; +} diff --git a/starters/saas/tests/requests/mod.rs b/starters/saas/tests/requests/mod.rs index 81ed68f96..887b7ce0b 100644 --- a/starters/saas/tests/requests/mod.rs +++ b/starters/saas/tests/requests/mod.rs @@ -1,4 +1,2 @@ mod auth; -mod notes; mod prepare_data; -mod user; diff --git a/starters/saas/tests/requests/notes.rs b/starters/saas/tests/requests/notes.rs deleted file mode 100644 index 7f74749f8..000000000 --- a/starters/saas/tests/requests/notes.rs +++ /dev/null @@ -1,123 +0,0 @@ -use insta::{assert_debug_snapshot, with_settings}; -use loco_rs::testing; -use loco_starter_template::{app::App, models::_entities::notes::Entity}; -use sea_orm::entity::prelude::*; -use serial_test::serial; - -// TODO: see how to dedup / extract this to app-local test utils -// not to framework, because that would require a runtime dep on insta -macro_rules! configure_insta { - ($($expr:expr),*) => { - let mut settings = insta::Settings::clone_current(); - settings.set_prepend_module_to_snapshot(false); - settings.set_snapshot_suffix("notes_request"); - let _guard = settings.bind_to_scope(); - }; -} - -#[tokio::test] -#[serial] -async fn can_get_notes() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let notes = request.get("/api/notes").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (notes.status_code(), notes.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_add_note() { - configure_insta!(); - - testing::request::(|request, _ctx| async move { - let payload = serde_json::json!({ - "title": "loco", - "content": "loco note test", - }); - - let add_note_request = request.post("/api/notes").json(&payload).await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (add_note_request.status_code(), add_note_request.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_get_note() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let add_note_request = request.get("/api/notes/1").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (add_note_request.status_code(), add_note_request.text()) - ); - }); - }) - .await; -} - -#[tokio::test] -#[serial] -async fn can_delete_note() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - testing::seed::(&ctx.db).await.unwrap(); - - let count_before_delete = Entity::find().all(&ctx.db).await.unwrap().len(); - let delete_note_request = request.delete("/api/notes/1").await; - - with_settings!({ - filters => { - let mut combined_filters = testing::CLEANUP_DATE.to_vec(); - combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]); - combined_filters - } - }, { - assert_debug_snapshot!( - (delete_note_request.status_code(), delete_note_request.text()) - ); - }); - - let count_after_delete = Entity::find().all(&ctx.db).await.unwrap().len(); - assert_eq!(count_after_delete, count_before_delete - 1); - }) - .await; -} diff --git a/starters/saas/tests/requests/snapshots/can_add_note@notes_request.snap b/starters/saas/tests/requests/snapshots/can_add_note@notes_request.snap deleted file mode 100644 index f8457d7a4..000000000 --- a/starters/saas/tests/requests/snapshots/can_add_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(add_note_request.status_code(), add_note_request.text())" ---- -( - 200, - "{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"loco\",\"content\":\"loco note test\"}", -) diff --git a/starters/saas/tests/requests/snapshots/can_delete_note@notes_request.snap b/starters/saas/tests/requests/snapshots/can_delete_note@notes_request.snap deleted file mode 100644 index 3481fc36d..000000000 --- a/starters/saas/tests/requests/snapshots/can_delete_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(delete_note_request.status_code(), delete_note_request.text())" ---- -( - 200, - "", -) diff --git a/starters/saas/tests/requests/snapshots/can_get_note@notes_request.snap b/starters/saas/tests/requests/snapshots/can_get_note@notes_request.snap deleted file mode 100644 index 8af1604c9..000000000 --- a/starters/saas/tests/requests/snapshots/can_get_note@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(add_note_request.status_code(), add_note_request.text())" ---- -( - 200, - "{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"Loco note 1 content\"}", -) diff --git a/starters/saas/tests/requests/snapshots/can_get_notes@notes_request.snap b/starters/saas/tests/requests/snapshots/can_get_notes@notes_request.snap deleted file mode 100644 index 014b75c03..000000000 --- a/starters/saas/tests/requests/snapshots/can_get_notes@notes_request.snap +++ /dev/null @@ -1,8 +0,0 @@ ---- -source: tests/requests/notes.rs -expression: "(notes.status_code(), notes.text())" ---- -( - 200, - "[{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"Loco note 1 content\"},{\"created_at\":\"DATEZ\",\"updated_at\":\"DATEZ\",\"id\":ID,\"title\":\"Loco note 2\",\"content\":\"Loco note 2 content\"}]", -) diff --git a/starters/saas/tests/requests/user.rs b/starters/saas/tests/requests/user.rs deleted file mode 100644 index ac924d9ae..000000000 --- a/starters/saas/tests/requests/user.rs +++ /dev/null @@ -1,40 +0,0 @@ -use insta::{assert_debug_snapshot, with_settings}; -use loco_rs::testing; -use loco_starter_template::app::App; -use serial_test::serial; - -use super::prepare_data; - -// TODO: see how to dedup / extract this to app-local test utils -// not to framework, because that would require a runtime dep on insta -macro_rules! configure_insta { - ($($expr:expr),*) => { - let mut settings = insta::Settings::clone_current(); - settings.set_prepend_module_to_snapshot(false); - settings.set_snapshot_suffix("auth_request"); - let _guard = settings.bind_to_scope(); - }; -} - -#[tokio::test] -#[serial] -async fn can_get_current_user() { - configure_insta!(); - - testing::request::(|request, ctx| async move { - let user = prepare_data::init_user_login(&request, &ctx).await; - - let (auth_key, auth_value) = prepare_data::auth_header(&user.token); - let response = request - .get("/api/user/current") - .add_header(auth_key, auth_value) - .await; - - with_settings!({ - filters => testing::cleanup_user_model() - }, { - assert_debug_snapshot!((response.status_code(), response.text())); - }); - }) - .await; -}