mirror of https://github.com/buster-so/buster.git
validation done.
This commit is contained in:
parent
aee795b07b
commit
32efa01b51
|
@ -99,7 +99,7 @@ models:
|
|||
expr: "created_at::date"
|
||||
type: "date"
|
||||
description: "Date when customer signed up"
|
||||
stored_values: true # Enable value caching
|
||||
searchable: true # Enable value caching
|
||||
|
||||
# Define measures
|
||||
measures:
|
||||
|
@ -197,7 +197,7 @@ dimensions:
|
|||
- name: country
|
||||
expr: "country_code"
|
||||
type: "string"
|
||||
stored_values: true # Values will be cached
|
||||
searchable: true # Values will be cached
|
||||
```
|
||||
|
||||
### Default SQL Generation
|
||||
|
|
|
@ -30,8 +30,11 @@ pub struct Model {
|
|||
schema: Option<String>,
|
||||
description: String,
|
||||
model: Option<String>,
|
||||
#[serde(default)]
|
||||
entities: Vec<Entity>,
|
||||
#[serde(default)]
|
||||
dimensions: Vec<Dimension>,
|
||||
#[serde(default)]
|
||||
measures: Vec<Measure>,
|
||||
}
|
||||
|
||||
|
@ -56,7 +59,7 @@ pub struct Dimension {
|
|||
dimension_type: String,
|
||||
description: String,
|
||||
#[serde(default = "bool::default")]
|
||||
stored_values: bool,
|
||||
searchable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
@ -334,16 +337,18 @@ impl ModelFile {
|
|||
|
||||
// If no project_path, the model should exist in the current project
|
||||
if entity.project_path.is_none() && !model_names.contains(referenced_model) {
|
||||
errors.push(format!(
|
||||
"Model '{}' references non-existent model '{}' in expression '{}'",
|
||||
model.name, referenced_model, entity.expr
|
||||
errors.push(format!(
|
||||
"Model '{}' references non-existent model '{}' (via {})",
|
||||
model.name,
|
||||
referenced_model,
|
||||
if entity.ref_.is_some() { "ref" } else { "name" }
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Warnings
|
||||
// Warnings
|
||||
for model in &self.model.models {
|
||||
if model.description.is_empty() {
|
||||
println!("⚠️ Warning: Model '{}' has no description", model.name);
|
||||
|
@ -571,18 +576,37 @@ impl ModelFile {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
println!("🔍 Searching for model '{}' in directory: {}",
|
||||
entity.ref_.as_ref().unwrap_or(&entity.name),
|
||||
target_path.display()
|
||||
);
|
||||
println!(" Found {} YAML files to search", model_files.len());
|
||||
|
||||
let mut found_model = false;
|
||||
for model_file in model_files {
|
||||
println!(" Checking file: {}", model_file.path().display());
|
||||
if let Ok(content) = std::fs::read_to_string(model_file.path()) {
|
||||
if let Ok(model_def) = serde_yaml::from_str::<BusterModel>(&content) {
|
||||
// Extract model name from entity.expr (assuming format "model_name.field")
|
||||
if let Some(referenced_model) = entity.ref_.as_ref().unwrap_or(&entity.name).split('.').next() {
|
||||
if model_def.models.iter().any(|m| m.name == referenced_model) {
|
||||
match serde_yaml::from_str::<BusterModel>(&content) {
|
||||
Ok(model_def) => {
|
||||
// Get the model reference from ref_ field if present, otherwise use name
|
||||
let referenced_model = entity.ref_.as_ref().unwrap_or(&entity.name);
|
||||
println!(" - Found {} models in file", model_def.models.len());
|
||||
for m in &model_def.models {
|
||||
println!(" - Checking model: {}", m.name);
|
||||
}
|
||||
if model_def.models.iter().any(|m| m.name == *referenced_model) {
|
||||
found_model = true;
|
||||
println!(" ✅ Found matching model!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!(" ⚠️ Failed to parse YAML content: {}", e);
|
||||
println!(" Content:\n{}", content);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!(" ⚠️ Failed to read file content");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -590,13 +614,14 @@ impl ModelFile {
|
|||
validation_errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::ModelNotFound,
|
||||
message: format!(
|
||||
"Referenced model from expression '{}' not found in project '{}'",
|
||||
entity.expr, project_path_display
|
||||
"Referenced model '{}' not found in project '{}'",
|
||||
entity.ref_.as_ref().unwrap_or(&entity.name),
|
||||
project_path_display
|
||||
),
|
||||
column_name: None,
|
||||
suggestion: Some(format!(
|
||||
"Verify that the model referenced in '{}' exists in the target project",
|
||||
entity.expr
|
||||
"Verify that the model '{}' exists in the target project",
|
||||
entity.ref_.as_ref().unwrap_or(&entity.name)
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
@ -611,8 +636,8 @@ impl ModelFile {
|
|||
suggestion: Some("Add data_source_name to the referenced project's buster.yml".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
}
|
||||
Err(e) => {
|
||||
validation_errors.push(ValidationError {
|
||||
error_type: ValidationErrorType::InvalidBusterYml,
|
||||
message: format!(
|
||||
|
@ -1037,8 +1062,8 @@ mod tests {
|
|||
|
||||
// Create buster.yml
|
||||
let buster_yml = r#"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
"#;
|
||||
create_test_yaml(temp_dir.path(), "buster.yml", buster_yml).await?;
|
||||
|
||||
|
@ -1206,21 +1231,21 @@ mod tests {
|
|||
// Create multiple model files
|
||||
for i in 1..=3 {
|
||||
let model_yml = format!(r#"
|
||||
version: 1
|
||||
models:
|
||||
version: 1
|
||||
models:
|
||||
- name: test_model_{}
|
||||
description: "Test model {}"
|
||||
entities: []
|
||||
dimensions:
|
||||
- name: dim1
|
||||
expr: "col1"
|
||||
type: "string"
|
||||
description: "First dimension"
|
||||
measures:
|
||||
- name: measure1
|
||||
expr: "col2"
|
||||
agg: "sum"
|
||||
description: "First measure"
|
||||
entities: []
|
||||
dimensions:
|
||||
- name: dim1
|
||||
expr: "col1"
|
||||
type: "string"
|
||||
description: "First dimension"
|
||||
measures:
|
||||
- name: measure1
|
||||
expr: "col2"
|
||||
agg: "sum"
|
||||
description: "First measure"
|
||||
"#, i, i);
|
||||
create_test_yaml(temp_dir.path(), &format!("test_model_{}.yml", i), &model_yml).await?;
|
||||
}
|
||||
|
@ -1238,8 +1263,8 @@ mod tests {
|
|||
|
||||
// Create buster.yml
|
||||
let buster_yml = r#"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
"#;
|
||||
create_test_yaml(temp_dir.path(), "buster.yml", buster_yml).await?;
|
||||
|
||||
|
@ -1260,8 +1285,8 @@ mod tests {
|
|||
|
||||
// Create buster.yml
|
||||
let buster_yml = r#"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
"#;
|
||||
create_test_yaml(temp_dir.path(), "buster.yml", buster_yml).await?;
|
||||
|
||||
|
@ -1308,7 +1333,7 @@ mod tests {
|
|||
// Create buster.yml
|
||||
let buster_yml = r#"
|
||||
data_source_name: "test_source"
|
||||
schema: "test_schema"
|
||||
schema: "test_schema"
|
||||
"#;
|
||||
create_test_yaml(temp_dir.path(), "buster.yml", buster_yml).await?;
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ pub struct Dimension {
|
|||
pub dimension_type: String,
|
||||
pub description: String,
|
||||
#[serde(default = "bool::default")]
|
||||
pub stored_values: bool,
|
||||
pub searchable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
|
Loading…
Reference in New Issue