Skip to content

Commit

Permalink
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
kaplanelad authored Jan 8, 2025
2 parents 512a496 + 1eb04fb commit d8baa3a
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 82 deletions.
2 changes: 2 additions & 0 deletions loco-gen/src/templates/migration/add_references.t
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ impl MigrationTrait for Migration {
{% for column in columns -%}
remove_column(m, "{{plural_snake}}", "{{column.0}}").await?;
{% endfor -%}

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ impl MigrationTrait for Migration {

async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
remove_reference(m, "posts", "user", "").await?;
}
Ok(())
}
}
6 changes: 1 addition & 5 deletions loco-new/tests/wizard/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ fn test_combination(
tester.run_generate_migration(&vec![
"CreatePosts",
"title:string",
"user:references",
"movies:references",
]);

Expand All @@ -208,10 +207,7 @@ fn test_combination(
]);

// Generate AddUserRefToPosts migration
// TODO:: not working on sqlite.
// - thread 'main' panicked at 'Sqlite doesn't support multiple alter options'
// - Sqlite does not support modification of foreign key constraints to existing
// tester.run_generate_migration(&vec!["AddUserRefToPosts", "movies:references"]);
tester.run_generate_migration(&vec!["AddUserRefToPosts", "users:references"]);

// Generate CreateJoinTableUsersAndGroups migration
tester.run_generate_migration(&vec!["CreateJoinTableUsersAndGroups", "count:int"]);
Expand Down
72 changes: 71 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ enum ComponentArg {
#[cfg(feature = "with-db")]
/// Generates a new model file for defining the data structure of your
/// application, and test file logic.
#[command(after_help = format!(
"{}
- Generate empty model:
$ cargo loco g model posts
- Generate model with fields:
$ cargo loco g model posts title:string! content:text
- Generate model with references:
$ cargo loco g model movies long_title:string director:references award:references:prize_id
# 'director:references' references the 'directors' table with 'director_id' on 'movies'
# 'award:references:prize_id' references the 'awards' table with 'prize_id' on 'movies'
",
"Examples:".bold().underline()
))]
Model {
/// Name of the thing to generate
name: String,
Expand All @@ -192,15 +207,48 @@ enum ComponentArg {
},
#[cfg(feature = "with-db")]
/// Generates a new migration file
#[command(after_help = format!("{}
- Create a new table:
$ cargo loco g migration CreatePosts title:string
# Creates a migration to add a 'posts' table with a 'title' column of type string.
- Add columns to an existing table:
$ cargo loco g migration AddNameAndAgeToUsers name:string age:int
# Adds 'name' (string) and 'age' (integer) columns to the 'users' table.
- Remove columns from a table:
$ cargo loco g migration RemoveNameAndAgeFromUsers name:string age:int
# Removes 'name' and 'age' columns from the 'users' table.
- Add a foreign key reference:
$ cargo loco g migration AddUserRefToPosts user:references
# Adds a reference to the 'users' table in the 'posts' table.
- Create a join table:
$ cargo loco g migration CreateJoinTableUsersAndGroups count:int
# Creates a join table 'users_groups' with an additional 'count' column.
- Create an empty migration:
$ cargo loco g migration FixUsersTable
# Creates a blank migration file for custom edits to the 'users' table.
After running the migration, follow these steps to complete the process:
- Apply the migration:
$ cargo loco db migrate
- Generate the model entities:
$ cargo loco db entities
", "Examples:".bold().underline()))]
Migration {
/// Name of the migration to generate
name: String,
/// Table fields, eg. title:string hits:int
#[clap(value_parser = parse_key_val::<String,String>)]
#[clap(value_parser = parse_key_val::<String,String>, )]
fields: Vec<(String, String)>,
},
#[cfg(feature = "with-db")]
/// Generates a CRUD scaffold, model and controller
#[command(after_help = format!("{}
$ cargo loco g model posts title:string! user:references --api", "Examples:".bold().underline()))]
Scaffold {
/// Name of the thing to generate
name: String,
Expand All @@ -226,6 +274,16 @@ enum ComponentArg {
api: bool,
},
/// Generate a new controller with the given controller name, and test file.
#[command(after_help = format!(
"{}
- Generate an empty controller:
$ cargo loco generate controller posts --api
- Generate a controller with actions:
$ cargo loco generate controller posts --api list remove update
",
"Examples:".bold().underline()
))]
Controller {
/// Name of the thing to generate
name: String,
Expand Down Expand Up @@ -274,6 +332,18 @@ enum ComponentArg {
},

/// Override templates and allows you to take control of them. You can always go back when deleting the local template.
#[command(after_help = format!("{}
- Override a Specific File:
* cargo loco generate override scaffold/api/controller.t
* cargo loco generate override migration/add_columns.t
- Override All Files in a Folder:
* cargo loco generate override scaffold/htmx
* cargo loco generate override task
- Override All templates:
* cargo loco generate override .
", "Examples:".bold().underline()))]
Override {
/// The path to a specific template or directory to copy.
template_path: Option<String>,
Expand Down
2 changes: 1 addition & 1 deletion src/controller/describe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::app::AppContext;
static DESCRIBE_METHOD_ACTION: OnceLock<Regex> = OnceLock::new();

fn get_describe_method_action() -> &'static Regex {
DESCRIBE_METHOD_ACTION.get_or_init(|| Regex::new(r"\b(\w+):\s*BoxedHandler\b").unwrap())
DESCRIBE_METHOD_ACTION.get_or_init(|| Regex::new(r"\b(\w+):\s*(BoxedHandler|Route)\b").unwrap())
}

/// Extract the allow list method actions from [`MethodRouter`].
Expand Down
83 changes: 41 additions & 42 deletions src/controller/middleware/cors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::time::Duration;
use axum::Router as AXRouter;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tower_http::cors;
use tower_http::cors::{self, Any};

use crate::{app::AppContext, controller::middleware::MiddlewareLayer, Result};

Expand Down Expand Up @@ -38,14 +38,8 @@ pub struct Cors {
pub vary: Vec<String>,
}

impl Default for Cors {
fn default() -> Self {
serde_json::from_value(json!({})).unwrap()
}
}

fn default_allow_origins() -> Vec<String> {
vec!["any".to_string()]
vec!["*".to_string()]
}

fn default_allow_headers() -> Vec<String> {
Expand All @@ -64,19 +58,13 @@ fn default_vary_headers() -> Vec<String> {
]
}

impl Cors {
#[must_use]
pub fn empty() -> Self {
Self {
enable: true,
allow_headers: vec![],
allow_methods: vec![],
allow_origins: vec![],
allow_credentials: false,
max_age: None,
vary: vec![],
}
impl Default for Cors {
fn default() -> Self {
serde_json::from_value(json!({})).unwrap()
}
}

impl Cors {
/// Creates cors layer
///
/// # Errors
Expand All @@ -93,35 +81,46 @@ impl Cors {
/// In all of these cases, the error returned will be the result of the
/// `parse` method of the corresponding type.
pub fn cors(&self) -> Result<cors::CorsLayer> {
let mut cors: cors::CorsLayer = cors::CorsLayer::permissive();

let mut list = vec![];
let mut cors: cors::CorsLayer = cors::CorsLayer::new();

// testing CORS, assuming https://example.com in the allow list:
// $ curl -v --request OPTIONS 'localhost:5150/api/_ping' -H 'Origin: https://example.com' -H 'Acces
// look for '< access-control-allow-origin: https://example.com' in response.
// if it doesn't appear (test with a bogus domain), it is not allowed.
for origin in &self.allow_origins {
list.push(origin.parse()?);
}
if !list.is_empty() {
cors = cors.allow_origin(list);
if self.allow_origins == default_allow_origins() {
cors = cors.allow_origin(Any);
} else {
let mut list = vec![];
for origin in &self.allow_origins {
list.push(origin.parse()?);
}
if !list.is_empty() {
cors = cors.allow_origin(list);
}
}

let mut list = vec![];
for header in &self.allow_headers {
list.push(header.parse()?);
}
if !list.is_empty() {
cors = cors.allow_headers(list);
if self.allow_headers == default_allow_headers() {
cors = cors.allow_headers(Any);
} else {
let mut list = vec![];
for header in &self.allow_headers {
list.push(header.parse()?);
}
if !list.is_empty() {
cors = cors.allow_headers(list);
}
}

let mut list = vec![];
for method in &self.allow_methods {
list.push(method.parse()?);
}
if !list.is_empty() {
cors = cors.allow_methods(list);
if self.allow_methods == default_allow_methods() {
cors = cors.allow_methods(Any);
} else {
let mut list = vec![];
for method in &self.allow_methods {
list.push(method.parse()?);
}
if !list.is_empty() {
cors = cors.allow_methods(list);
}
}

let mut list = vec![];
Expand Down Expand Up @@ -192,7 +191,7 @@ mod tests {
#[case] allow_methods: Option<Vec<String>>,
#[case] max_age: Option<u64>,
) {
let mut middleware = Cors::empty();
let mut middleware = Cors::default();
if let Some(allow_headers) = allow_headers {
middleware.allow_headers = allow_headers;
}
Expand Down Expand Up @@ -238,7 +237,7 @@ mod tests {

#[tokio::test]
async fn cors_options() {
let mut middleware = Cors::empty();
let mut middleware = Cors::default();
middleware.allow_origins = vec![
"http://localhost:8080".to_string(),
"http://example.com".to_string(),
Expand Down
Loading

0 comments on commit d8baa3a

Please sign in to comment.