Skip to content

Commit 377a3af

Browse files
committed
pr feedback
Signed-off-by: Daniel Gerlag <daniel@gerlag.ca>
1 parent 1edf50f commit 377a3af

4 files changed

Lines changed: 145 additions & 73 deletions

File tree

build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const VENDOR_TAG: &str = "v1";
99
const VENDORED_TARGETS: &[&str] = &["x86_64-pc-windows-msvc"];
1010

1111
fn main() {
12+
// Declare env vars and files this build script depends on so Cargo
13+
// reruns it when they change.
14+
println!("cargo:rerun-if-changed=Cargo.lock");
15+
println!("cargo:rerun-if-env-changed=TARGET");
16+
1217
let rustc_version = Command::new("rustc")
1318
.arg("--version")
1419
.output()

src/plugin/remove.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,31 @@ pub fn remove(reference: &str, plugins_dir: &std::path::Path) -> Result<()> {
7272
std::process::exit(1);
7373
}
7474

75-
// Update lockfile: remove the entry
75+
// Update lockfile: remove the entry by key or by matching filename
7676
let lockfile_dir = plugins_dir;
7777
if let Ok(Some(mut lockfile)) = PluginLockfile::read(lockfile_dir) {
78+
let mut lockfile_changed = false;
79+
80+
// Try exact key match first
7881
if lockfile.remove(reference).is_some() {
82+
lockfile_changed = true;
83+
}
84+
85+
// Also remove any entry whose filename matches a file we just deleted,
86+
// in case the plugin was installed under a different key
87+
if !lockfile_changed {
88+
let removed_keys: Vec<String> = lockfile
89+
.iter()
90+
.filter(|(_, entry)| !plugins_dir.join(&entry.filename).exists())
91+
.map(|(key, _)| key.clone())
92+
.collect();
93+
for key in removed_keys {
94+
lockfile.remove(&key);
95+
lockfile_changed = true;
96+
}
97+
}
98+
99+
if lockfile_changed {
79100
let _ = lockfile.write(lockfile_dir);
80101
println!("{}", cli_styles::detail("Updated plugins.lock"));
81102
}

src/plugin/upgrade.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,22 @@ pub async fn upgrade(
125125
continue;
126126
}
127127

128-
let base_ref = ref_key.split(':').next().unwrap_or(ref_key);
128+
// Strip the tag/digest suffix to get the base image reference.
129+
// OCI refs can be: registry:port/repo:tag or registry:port/repo@sha256:...
130+
// We strip only after the last `/` to avoid truncating port numbers.
131+
let base_ref = if let Some(at_pos) = ref_key.rfind('@') {
132+
&ref_key[..at_pos]
133+
} else if let Some(slash_pos) = ref_key.rfind('/') {
134+
if let Some(colon_pos) = ref_key[slash_pos..].find(':') {
135+
&ref_key[..slash_pos + colon_pos]
136+
} else {
137+
ref_key
138+
}
139+
} else if let Some(colon_pos) = ref_key.find(':') {
140+
&ref_key[..colon_pos]
141+
} else {
142+
ref_key
143+
};
129144
let sp = cli_styles::spinner(&format!("Checking {ref_key}..."));
130145

131146
match resolver.resolve(base_ref, &registry_url).await {

tests/dto_camelcase_test.rs

Lines changed: 102 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,138 @@
1-
// Test to verify that DTO fields serialize as camelCase
1+
// Test to verify that DTO fields serialize as camelCase.
2+
//
3+
// These tests construct real Rust DTO types and verify that serde
4+
// serializes their fields using camelCase, catching regressions in
5+
// the `#[serde(rename_all = "camelCase")]` annotations.
26

3-
use serde_json::json;
7+
use drasi_lib::config::QueryLanguage;
8+
use drasi_server::api::models::observability::{
9+
ComponentEventDto, ComponentStatusDto, ComponentTypeDto, LogLevelDto, LogMessageDto,
10+
};
11+
use drasi_server::api::models::queries::query::{QueryConfigDto, SourceSubscriptionConfigDto};
412

513
#[test]
6-
fn test_postgres_dto_serializes_camelcase() {
7-
let json = json!({
8-
"host": "localhost",
9-
"port": 5432,
10-
"database": "testdb",
11-
"user": "testuser",
12-
"password": "testpass",
13-
"tables": [],
14-
"slotName": "test_slot",
15-
"publicationName": "test_pub",
16-
"sslMode": "disable",
17-
"tableKeys": []
18-
});
14+
fn test_query_config_dto_serializes_camelcase() {
15+
let dto = QueryConfigDto {
16+
id: "test-query".to_string(),
17+
auto_start: true,
18+
query: "MATCH (n) RETURN n".to_string(),
19+
query_language: QueryLanguage::Cypher,
20+
middleware: vec![],
21+
sources: vec![SourceSubscriptionConfigDto {
22+
source_id: "my-source".to_string(),
23+
nodes: vec![],
24+
relations: vec![],
25+
pipeline: vec![],
26+
}],
27+
enable_bootstrap: true,
28+
bootstrap_buffer_size: 500,
29+
joins: None,
30+
priority_queue_capacity: Some(1000),
31+
dispatch_buffer_capacity: None,
32+
dispatch_mode: None,
33+
storage_backend: None,
34+
};
1935

20-
// Verify fields are in camelCase
21-
assert!(json.get("slotName").is_some(), "slotName should exist");
36+
let json = serde_json::to_value(&dto).unwrap();
37+
38+
// camelCase fields must exist
39+
assert!(json.get("autoStart").is_some(), "autoStart should exist");
40+
assert!(
41+
json.get("queryLanguage").is_some(),
42+
"queryLanguage should exist"
43+
);
44+
assert!(
45+
json.get("enableBootstrap").is_some(),
46+
"enableBootstrap should exist"
47+
);
2248
assert!(
23-
json.get("publicationName").is_some(),
24-
"publicationName should exist"
49+
json.get("bootstrapBufferSize").is_some(),
50+
"bootstrapBufferSize should exist"
51+
);
52+
assert!(
53+
json.get("priorityQueueCapacity").is_some(),
54+
"priorityQueueCapacity should exist"
2555
);
26-
assert!(json.get("tableKeys").is_some(), "tableKeys should exist");
27-
assert!(json.get("sslMode").is_some(), "sslMode should exist");
2856

29-
// Verify snake_case versions don't exist
57+
// snake_case equivalents must NOT exist
3058
assert!(
31-
json.get("slot_name").is_none(),
32-
"slot_name should NOT exist"
59+
json.get("auto_start").is_none(),
60+
"auto_start should NOT exist"
3361
);
3462
assert!(
35-
json.get("publication_name").is_none(),
36-
"publication_name should NOT exist"
63+
json.get("query_language").is_none(),
64+
"query_language should NOT exist"
3765
);
3866
assert!(
39-
json.get("table_keys").is_none(),
40-
"table_keys should NOT exist"
67+
json.get("enable_bootstrap").is_none(),
68+
"enable_bootstrap should NOT exist"
4169
);
42-
assert!(json.get("ssl_mode").is_none(), "ssl_mode should NOT exist");
4370

44-
println!("✅ Postgres source config serializes as camelCase");
71+
// Nested source subscription DTO
72+
let source = json["sources"].as_array().unwrap().first().unwrap();
73+
assert!(source.get("sourceId").is_some(), "sourceId should exist");
74+
assert!(
75+
source.get("source_id").is_none(),
76+
"source_id should NOT exist"
77+
);
4578
}
4679

4780
#[test]
48-
fn test_mock_dto_serializes_camelcase() {
49-
let json = json!({
50-
"dataType": {"type": "sensorReading", "sensorCount": 5},
51-
"intervalMs": 1000
52-
});
81+
fn test_component_event_dto_serializes_camelcase() {
82+
let dto = ComponentEventDto {
83+
component_id: "my-source".to_string(),
84+
component_type: ComponentTypeDto::Source,
85+
status: ComponentStatusDto::Running,
86+
timestamp: chrono::Utc::now(),
87+
message: Some("started".to_string()),
88+
};
5389

54-
// Verify fields are in camelCase
55-
assert!(json.get("dataType").is_some(), "dataType should exist");
56-
assert!(json.get("intervalMs").is_some(), "intervalMs should exist");
90+
let json = serde_json::to_value(&dto).unwrap();
5791

58-
// Verify snake_case versions don't exist
5992
assert!(
60-
json.get("data_type").is_none(),
61-
"data_type should NOT exist"
93+
json.get("componentId").is_some(),
94+
"componentId should exist"
6295
);
6396
assert!(
64-
json.get("interval_ms").is_none(),
65-
"interval_ms should NOT exist"
97+
json.get("componentType").is_some(),
98+
"componentType should exist"
99+
);
100+
assert!(
101+
json.get("component_id").is_none(),
102+
"component_id should NOT exist"
103+
);
104+
assert!(
105+
json.get("component_type").is_none(),
106+
"component_type should NOT exist"
66107
);
67-
68-
println!("✅ Mock source config serializes as camelCase");
69108
}
70109

71110
#[test]
72-
fn test_http_source_dto_serializes_camelcase() {
73-
let json = json!({
74-
"host": "localhost",
75-
"port": 8080,
76-
"timeoutMs": 5000,
77-
"adaptiveMaxBatchSize": 100,
78-
"adaptiveMinBatchSize": 10,
79-
"adaptiveMaxWaitMs": 500,
80-
"adaptiveMinWaitMs": 10,
81-
"adaptiveWindowSecs": 60,
82-
"adaptiveEnabled": true
83-
});
111+
fn test_log_message_dto_serializes_camelcase() {
112+
let dto = LogMessageDto {
113+
timestamp: chrono::Utc::now(),
114+
level: LogLevelDto::Info,
115+
message: "test".to_string(),
116+
component_id: "my-source".to_string(),
117+
component_type: ComponentTypeDto::Source,
118+
};
119+
120+
let json = serde_json::to_value(&dto).unwrap();
84121

85-
// Verify fields are in camelCase
86-
assert!(json.get("timeoutMs").is_some(), "timeoutMs should exist");
87122
assert!(
88-
json.get("adaptiveMaxBatchSize").is_some(),
89-
"adaptiveMaxBatchSize should exist"
123+
json.get("componentId").is_some(),
124+
"componentId should exist"
90125
);
91126
assert!(
92-
json.get("adaptiveMinBatchSize").is_some(),
93-
"adaptiveMinBatchSize should exist"
127+
json.get("componentType").is_some(),
128+
"componentType should exist"
94129
);
95-
96-
// Verify snake_case versions don't exist
97130
assert!(
98-
json.get("timeout_ms").is_none(),
99-
"timeout_ms should NOT exist"
131+
json.get("component_id").is_none(),
132+
"component_id should NOT exist"
100133
);
101134
assert!(
102-
json.get("adaptive_max_batch_size").is_none(),
103-
"adaptive_max_batch_size should NOT exist"
135+
json.get("component_type").is_none(),
136+
"component_type should NOT exist"
104137
);
105-
106-
println!("✅ HTTP source config serializes as camelCase");
107138
}

0 commit comments

Comments
 (0)