pub const MOD: &str = r#"mod entity;
mod module;
mod service;
pub mod http;
pub use entity::*;
pub use module::{{module}};
pub use service::{{service}};
pub use http::{{{controller}}, {{http_module}}};
"#;
pub const ENTITY: &str = r#"use nest_rs_resource::expose;
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
// `soft_delete` + `timestamps` match the three columns `nestrs g migration`
// scaffolds for this table, and the `users/` exemplar. Without them the
// generated resource hard-deleted against a table carrying an unused
// `deleted_at` tombstone, and never wrote the audit columns — the exact silent
// mismatch /database/crud/ warns about. `timestamps` emits the
// `ActiveModelBehavior`, so the resource must not also write an empty one.
#[expose(
name = "{{entity}}",
service = super::service::{{service}},
soft_delete,
timestamps
)]
#[sea_orm::model]
#[derive(Clone, Debug, DeriveEntityModel)]
#[sea_orm(
table_name = "{{table}}",
model_attrs(derive(PartialEq, Serialize, Deserialize))
)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
#[expose]
pub id: Uuid,
#[expose(input(create, update), validate(length(min = 1)))]
pub name: String,
#[expose]
pub created_at: DateTimeWithTimeZone,
#[expose]
pub updated_at: DateTimeWithTimeZone,
pub deleted_at: Option<DateTimeWithTimeZone>,
}
"#;
pub const SERVICE: &str = r#"use nest_rs_core::injectable;
use nest_rs_seaorm::{Creatable, CrudService, Deletable, Updatable};
use super::entity::{{{create_op}}, Entity as {{pascal}}, {{update_op}}};
#[injectable]
#[derive(Default)]
pub struct {{service}};
// Read API + the audited ORM choke point.
impl CrudService for {{service}} {
type Entity = {{pascal}};
// The entity's `soft_delete` flag makes the column addressable; this
// override is what makes DELETE tombstone instead of hard-delete, and what
// ANDs `deleted_at IS NULL` into every read. Both halves are required —
// drop them together for a resource that should really erase rows.
fn soft_delete_column() -> Option<super::entity::Column> {
Some(super::entity::Column::DeletedAt)
}
}
// Opt-in write capabilities — drop any your resource does not offer (and the
// matching #[crud] op), instead of declaring a placeholder input type.
impl Creatable for {{service}} {
type Create = {{create_op}};
}
impl Updatable for {{service}} {
type Update = {{update_op}};
}
impl Deletable for {{service}} {}
"#;
pub const MODULE: &str = r#"use nest_rs_core::module;
use super::service::{{service}};
#[module(providers = [{{service}}])]
pub struct {{module}};
"#;
pub const HTTP_MOD: &str = r#"mod controller;
mod module;
pub use controller::{{controller}};
pub use module::{{http_module}};
"#;
pub const HTTP_MODULE: &str = r#"use nest_rs_core::module;
use super::controller::{{controller}};
use crate::authz::AuthzHttpModule;
use crate::{{snake}}::{{module}};
#[module(
imports = [{{module}}, AuthzHttpModule],
providers = [{{controller}}],
)]
pub struct {{http_module}};
"#;
pub const HTTP_CONTROLLER: &str = r#"use std::sync::Arc;
use nest_rs_http::{controller, crud};
use crate::authn::AuthnGuard;
use crate::authz::AuthzGuard;
use crate::{{snake}}::{{{create_op}}, Entity as {{entity}}Entity, {{entity}}, {{service}}, {{update_op}}};
#[controller(path = "/{{kebab}}")]
#[use_guards(AuthnGuard, AuthzGuard)]
pub struct {{controller}} {
#[inject]
svc: Arc<{{service}}>,
}
// Every operation is authenticated, ability-filtered, transactional, and
// field-masked — declared by the two guards, generated by #[crud]. The routes
// answer 403 until AppAbility grants a rule for {{entity}}.
#[crud(
service = svc,
entity = {{entity}}Entity,
output = {{entity}},
create = {{create_op}},
update = {{update_op}},
)]
impl {{controller}} {}
"#;