Skip to content

Commit 600f273

Browse files
committed
refactor: no manual url building
1 parent 6babd1a commit 600f273

File tree

9 files changed

+41
-78
lines changed

9 files changed

+41
-78
lines changed

crates/pgls_diagnostics_categories/src/categories.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ define_categories! {
5252
"splinter/performance/duplicateIndex": "https://supabase.com/docs/guides/database/database-linter?lint=0009_duplicate_index",
5353
"splinter/performance/multiplePermissivePolicies": "https://supabase.com/docs/guides/database/database-linter?lint=0006_multiple_permissive_policies",
5454
"splinter/performance/noPrimaryKey": "https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key",
55-
"splinter/performance/tableBloat": "https://supabase.com/docs/guides/database/database-linter?lint=0020_table_bloat",
55+
"splinter/performance/tableBloat": "https://supabase.com/docs/guides/database/database-linter",
5656
"splinter/performance/unindexedForeignKeys": "https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys",
5757
"splinter/performance/unusedIndex": "https://supabase.com/docs/guides/database/database-linter?lint=0005_unused_index",
5858
"splinter/security/authUsersExposed": "https://supabase.com/docs/guides/database/database-linter?lint=0002_auth_users_exposed",
5959
"splinter/security/extensionInPublic": "https://supabase.com/docs/guides/database/database-linter?lint=0014_extension_in_public",
6060
"splinter/security/extensionVersionsOutdated": "https://supabase.com/docs/guides/database/database-linter?lint=0022_extension_versions_outdated",
61-
"splinter/security/fkeyToAuthUnique": "https://supabase.com/docs/guides/database/database-linter?lint=0021_fkey_to_auth_unique",
61+
"splinter/security/fkeyToAuthUnique": "https://supabase.com/docs/guides/database/database-linter",
6262
"splinter/security/foreignTableInApi": "https://supabase.com/docs/guides/database/database-linter?lint=0017_foreign_table_in_api",
6363
"splinter/security/functionSearchPathMutable": "https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable",
6464
"splinter/security/insecureQueueExposedInApi": "https://supabase.com/docs/guides/database/database-linter?lint=0019_insecure_queue_exposed_in_api",

crates/pgls_splinter/TODO.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

crates/pgls_splinter/src/convert.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,45 +27,13 @@ impl From<SplinterQueryResult> for SplinterDiagnostic {
2727
schema,
2828
object_name,
2929
object_type,
30-
remediation_url: build_remediation_url(&result.name),
30+
remediation: result.remediation,
3131
additional_metadata,
3232
},
3333
}
3434
}
3535
}
3636

37-
/// Build remediation URL from rule name
38-
/// Maps rule names to their Supabase linter documentation
39-
fn build_remediation_url(name: &str) -> String {
40-
// Map rule names to their lint IDs
41-
let lint_id = match name {
42-
"unindexed_foreign_keys" => "0001_unindexed_foreign_keys",
43-
"auth_users_exposed" => "0002_auth_users_exposed",
44-
"auth_rls_initplan" => "0003_auth_rls_initplan",
45-
"no_primary_key" => "0004_no_primary_key",
46-
"unused_index" => "0005_unused_index",
47-
"multiple_permissive_policies" => "0006_multiple_permissive_policies",
48-
"policy_exists_rls_disabled" => "0007_policy_exists_rls_disabled",
49-
"rls_enabled_no_policy" => "0008_rls_enabled_no_policy",
50-
"duplicate_index" => "0009_duplicate_index",
51-
"security_definer_view" => "0010_security_definer_view",
52-
"function_search_path_mutable" => "0011_function_search_path_mutable",
53-
"rls_disabled_in_public" => "0013_rls_disabled_in_public",
54-
"extension_in_public" => "0014_extension_in_public",
55-
"rls_references_user_metadata" => "0015_rls_references_user_metadata",
56-
"materialized_view_in_api" => "0016_materialized_view_in_api",
57-
"foreign_table_in_api" => "0017_foreign_table_in_api",
58-
"unsupported_reg_types" => "unsupported_reg_types",
59-
"insecure_queue_exposed_in_api" => "0019_insecure_queue_exposed_in_api",
60-
"table_bloat" => "0020_table_bloat",
61-
"fkey_to_auth_unique" => "0021_fkey_to_auth_unique",
62-
"extension_versions_outdated" => "0022_extension_versions_outdated",
63-
_ => return "https://supabase.com/docs/guides/database/database-linter".to_string(),
64-
};
65-
66-
format!("https://supabase.com/docs/guides/database/database-linter?lint={lint_id}")
67-
}
68-
6937
/// Parse severity level from the query result
7038
fn parse_severity(level: &str) -> Severity {
7139
match level {
@@ -79,6 +47,7 @@ fn parse_severity(level: &str) -> Severity {
7947
/// Convert rule name and group to a Category
8048
/// Note: Rule names use snake_case, but categories use camelCase
8149
fn rule_name_to_category(name: &str, group: &str) -> &'static Category {
50+
// we cannot use convert_case here because category! macro requires a string literal
8251
match (group, name) {
8352
("performance", "unindexed_foreign_keys") => {
8453
category!("splinter/performance/unindexedForeignKeys")

crates/pgls_splinter/src/diagnostics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub struct SplinterAdvices {
4141
pub object_type: Option<String>,
4242

4343
/// URL to documentation/remediation guide
44-
pub remediation_url: String,
44+
pub remediation: String,
4545

4646
/// Additional rule-specific metadata (e.g., fkey_name, column, indexes)
4747
/// This contains fields that don't fit into the common structure
@@ -70,10 +70,10 @@ impl Advices for SplinterAdvices {
7070
}
7171
}
7272

73-
// Show remediation URL
73+
// Show remediation
7474
visitor.record_log(
7575
LogCategory::Info,
76-
&format!("Documentation: {}", &self.remediation_url),
76+
&format!("Remediation: {}", &self.remediation),
7777
)?;
7878

7979
Ok(())

crates/pgls_splinter/tests/snapshots/multiple_issues.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Advices:
1010
Identifies foreign key constraints without a covering index, which can impact database performance.
1111
[Info] table: public.child_table
1212
{&quot;fkey_name&quot;:&quot;child_table_parent_id_fkey&quot;,&quot;fkey_columns&quot;:[2]}
13-
[Info] Documentation: https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys
13+
[Info] Remediation: https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys
1414
1515
---
1616
@@ -20,4 +20,4 @@ Message: Table \`public.no_pk_table\` does not have a primary key
2020
Advices:
2121
Detects if a table does not have a primary key. Tables without a primary key can be inefficient to interact with at scale.
2222
[Info] table: public.no_pk_table
23-
[Info] Documentation: https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key
23+
[Info] Remediation: https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key

crates/pgls_splinter/tests/snapshots/no_primary_key.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Message: Table \`public.articles\` does not have a primary key
99
Advices:
1010
Detects if a table does not have a primary key. Tables without a primary key can be inefficient to interact with at scale.
1111
[Info] table: public.articles
12-
[Info] Documentation: https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key
12+
[Info] Remediation: https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key

crates/pgls_splinter/tests/snapshots/policy_exists_rls_disabled.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Message: Table \`public.documents\` has RLS policies but RLS is not enabled on t
99
Advices:
1010
Detects cases where row level security (RLS) policies have been created, but RLS has not been enabled for the underlying table.
1111
[Info] table: public.documents
12-
[Info] Documentation: https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled
12+
[Info] Remediation: https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled

crates/pgls_splinter/tests/snapshots/unindexed_foreign_key.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ Advices:
1010
Identifies foreign key constraints without a covering index, which can impact database performance.
1111
[Info] table: public.posts
1212
{&quot;fkey_name&quot;:&quot;posts_user_id_fkey&quot;,&quot;fkey_columns&quot;:[2]}
13-
[Info] Documentation: https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys
13+
[Info] Remediation: https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys

xtask/codegen/src/generate_splinter.rs

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,26 @@ fn extract_rules_from_sql(content: &str) -> Result<BTreeMap<String, RuleInfo>> {
4444
// Look for pattern: 'rule_name' as "name!",
4545
if line.contains(" as \"name!\"") {
4646
if let Some(name) = extract_string_literal(line) {
47-
// Look ahead for categories
47+
// Look ahead for categories and remediation URL
4848
let mut categories = None;
49+
let mut remediation_url = None;
4950

5051
for next_line in lines[i..].iter().take(30) {
5152
let next_line = next_line.trim();
5253

5354
// Extract categories from pattern: array['CATEGORY'] as "categories!",
5455
if next_line.contains(" as \"categories!\"") {
5556
categories = extract_categories(next_line);
56-
break; // Stop once we have categories
57+
}
58+
59+
// Extract remediation URL from pattern: 'url' as "remediation!",
60+
if next_line.contains(" as \"remediation!\"") {
61+
remediation_url = extract_string_literal(next_line);
62+
}
63+
64+
// Stop once we have both
65+
if categories.is_some() && remediation_url.is_some() {
66+
break;
5767
}
5868
}
5969

@@ -66,6 +76,7 @@ fn extract_rules_from_sql(content: &str) -> Result<BTreeMap<String, RuleInfo>> {
6676
snake_case: name.clone(),
6777
camel_case: snake_to_camel_case(&name),
6878
categories: cats,
79+
url: remediation_url,
6980
},
7081
);
7182
}
@@ -79,6 +90,7 @@ fn extract_rules_from_sql(content: &str) -> Result<BTreeMap<String, RuleInfo>> {
7990
snake_case: "unknown".to_string(),
8091
camel_case: "unknown".to_string(),
8192
categories: vec!["UNKNOWN".to_string()],
93+
url: None,
8294
},
8395
);
8496

@@ -137,42 +149,17 @@ fn snake_to_camel_case(s: &str) -> String {
137149
Case::Camel.convert(s)
138150
}
139151

152+
/// Check if a string is a valid URL (simple check for http/https)
153+
fn is_valid_url(s: &str) -> bool {
154+
s.starts_with("http://") || s.starts_with("https://")
155+
}
156+
140157
struct RuleInfo {
141158
#[allow(dead_code)]
142159
snake_case: String,
143160
camel_case: String,
144161
categories: Vec<String>,
145-
}
146-
147-
/// Build remediation URL from rule name
148-
/// Must match the logic in crates/pgls_splinter/src/convert.rs
149-
fn build_remediation_url(name: &str) -> String {
150-
let lint_id = match name {
151-
"unindexed_foreign_keys" => "0001_unindexed_foreign_keys",
152-
"auth_users_exposed" => "0002_auth_users_exposed",
153-
"auth_rls_initplan" => "0003_auth_rls_initplan",
154-
"no_primary_key" => "0004_no_primary_key",
155-
"unused_index" => "0005_unused_index",
156-
"multiple_permissive_policies" => "0006_multiple_permissive_policies",
157-
"policy_exists_rls_disabled" => "0007_policy_exists_rls_disabled",
158-
"rls_enabled_no_policy" => "0008_rls_enabled_no_policy",
159-
"duplicate_index" => "0009_duplicate_index",
160-
"security_definer_view" => "0010_security_definer_view",
161-
"function_search_path_mutable" => "0011_function_search_path_mutable",
162-
"rls_disabled_in_public" => "0013_rls_disabled_in_public",
163-
"extension_in_public" => "0014_extension_in_public",
164-
"rls_references_user_metadata" => "0015_rls_references_user_metadata",
165-
"materialized_view_in_api" => "0016_materialized_view_in_api",
166-
"foreign_table_in_api" => "0017_foreign_table_in_api",
167-
"unsupported_reg_types" => "unsupported_reg_types",
168-
"insecure_queue_exposed_in_api" => "0019_insecure_queue_exposed_in_api",
169-
"table_bloat" => "0020_table_bloat",
170-
"fkey_to_auth_unique" => "0021_fkey_to_auth_unique",
171-
"extension_versions_outdated" => "0022_extension_versions_outdated",
172-
_ => return "https://supabase.com/docs/guides/database/database-linter".to_string(),
173-
};
174-
175-
format!("https://supabase.com/docs/guides/database/database-linter?lint={lint_id}")
162+
url: Option<String>,
176163
}
177164

178165
/// Update the categories.rs file with splinter rules
@@ -190,7 +177,15 @@ fn update_categories_file(rules: BTreeMap<String, RuleInfo>) -> Result<()> {
190177
// In practice, splinter rules have only one category
191178
rule.categories.iter().map(|category| {
192179
let group = category.to_lowercase();
193-
let url = build_remediation_url(&rule.snake_case);
180+
181+
// Use extracted URL if it's a valid URL, otherwise fallback to default
182+
let url = rule
183+
.url
184+
.as_ref()
185+
.filter(|u| is_valid_url(u))
186+
.map(|u| u.as_str())
187+
.unwrap_or("https://supabase.com/docs/guides/database/database-linter");
188+
194189
(
195190
group.clone(),
196191
format!(

0 commit comments

Comments
 (0)