making linters happy
Some checks failed
CI / Rust Blocking Checks (push) Failing after 22s
CI / Web Blocking Checks (push) Failing after 26s
CI / Security Blocking Checks (push) Successful in 9s
CI / Web Advisory Checks (push) Successful in 32s
CI / Security Advisory Checks (push) Has been cancelled
Some checks failed
CI / Rust Blocking Checks (push) Failing after 22s
CI / Web Blocking Checks (push) Failing after 26s
CI / Security Blocking Checks (push) Successful in 9s
CI / Web Advisory Checks (push) Successful in 32s
CI / Security Advisory Checks (push) Has been cancelled
This commit is contained in:
@@ -45,21 +45,16 @@ pub struct Claims {
|
||||
pub metadata: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TokenType {
|
||||
#[default]
|
||||
Access,
|
||||
Refresh,
|
||||
Sensor,
|
||||
Execution,
|
||||
}
|
||||
|
||||
impl Default for TokenType {
|
||||
fn default() -> Self {
|
||||
Self::Access
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for JWT tokens
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JwtConfig {
|
||||
@@ -247,11 +242,7 @@ pub fn validate_token(token: &str, config: &JwtConfig) -> Result<Claims, JwtErro
|
||||
|
||||
/// Extract token from Authorization header
|
||||
pub fn extract_token_from_header(auth_header: &str) -> Option<&str> {
|
||||
if auth_header.starts_with("Bearer ") {
|
||||
Some(&auth_header[7..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
auth_header.strip_prefix("Bearer ")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -45,21 +45,16 @@ pub mod enums {
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// How parameters should be delivered to an action
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ParameterDelivery {
|
||||
/// Pass parameters via stdin (secure, recommended for most cases)
|
||||
#[default]
|
||||
Stdin,
|
||||
/// Pass parameters via temporary file (secure, best for large payloads)
|
||||
File,
|
||||
}
|
||||
|
||||
impl Default for ParameterDelivery {
|
||||
fn default() -> Self {
|
||||
Self::Stdin
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParameterDelivery {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@@ -99,31 +94,23 @@ pub mod enums {
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||
Ok(<String as sqlx::Encode<sqlx::Postgres>>::encode(
|
||||
self.to_string(),
|
||||
buf,
|
||||
)?)
|
||||
<String as sqlx::Encode<sqlx::Postgres>>::encode(self.to_string(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Format for parameter serialization
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ParameterFormat {
|
||||
/// KEY='VALUE' format (one per line)
|
||||
Dotenv,
|
||||
/// JSON object
|
||||
#[default]
|
||||
Json,
|
||||
/// YAML format
|
||||
Yaml,
|
||||
}
|
||||
|
||||
impl Default for ParameterFormat {
|
||||
fn default() -> Self {
|
||||
Self::Json
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParameterFormat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@@ -165,18 +152,16 @@ pub mod enums {
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||
Ok(<String as sqlx::Encode<sqlx::Postgres>>::encode(
|
||||
self.to_string(),
|
||||
buf,
|
||||
)?)
|
||||
<String as sqlx::Encode<sqlx::Postgres>>::encode(self.to_string(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Format for action output parsing
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OutputFormat {
|
||||
/// Plain text (no parsing)
|
||||
#[default]
|
||||
Text,
|
||||
/// Parse as JSON
|
||||
Json,
|
||||
@@ -186,12 +171,6 @@ pub mod enums {
|
||||
Jsonl,
|
||||
}
|
||||
|
||||
impl Default for OutputFormat {
|
||||
fn default() -> Self {
|
||||
Self::Text
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OutputFormat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@@ -235,10 +214,7 @@ pub mod enums {
|
||||
&self,
|
||||
buf: &mut sqlx::postgres::PgArgumentBuffer,
|
||||
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||
Ok(<String as sqlx::Encode<sqlx::Postgres>>::encode(
|
||||
self.to_string(),
|
||||
buf,
|
||||
)?)
|
||||
<String as sqlx::Encode<sqlx::Postgres>>::encode(self.to_string(), buf)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,20 +347,17 @@ pub mod enums {
|
||||
/// - `Public`: viewable by all authenticated users on the platform.
|
||||
/// - `Private`: restricted based on the artifact's `scope` and `owner` fields.
|
||||
/// Full RBAC enforcement is deferred; for now the field enables filtering.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, ToSchema)]
|
||||
#[derive(
|
||||
Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, Type, ToSchema,
|
||||
)]
|
||||
#[sqlx(type_name = "artifact_visibility_enum", rename_all = "lowercase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ArtifactVisibility {
|
||||
Public,
|
||||
#[default]
|
||||
Private,
|
||||
}
|
||||
|
||||
impl Default for ArtifactVisibility {
|
||||
fn default() -> Self {
|
||||
Self::Private
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, ToSchema)]
|
||||
#[sqlx(type_name = "workflow_task_status_enum", rename_all = "lowercase")]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
|
||||
@@ -141,7 +141,7 @@ mod tests {
|
||||
fn test_message_queue_creation() {
|
||||
// This test just verifies the struct can be instantiated
|
||||
// Actual connection tests require a running RabbitMQ instance
|
||||
assert!(true);
|
||||
// (nothing to assert without a live broker)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -69,20 +69,15 @@ use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// Message delivery mode
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DeliveryMode {
|
||||
/// Non-persistent messages (faster, but may be lost on broker restart)
|
||||
NonPersistent = 1,
|
||||
/// Persistent messages (slower, but survive broker restart)
|
||||
#[default]
|
||||
Persistent = 2,
|
||||
}
|
||||
|
||||
impl Default for DeliveryMode {
|
||||
fn default() -> Self {
|
||||
Self::Persistent
|
||||
}
|
||||
}
|
||||
|
||||
/// Message priority (0-9, higher is more urgent)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Priority(u8);
|
||||
@@ -125,25 +120,21 @@ impl fmt::Display for Priority {
|
||||
}
|
||||
|
||||
/// Message acknowledgment mode
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AckMode {
|
||||
/// Automatically acknowledge messages after delivery
|
||||
Auto,
|
||||
/// Manually acknowledge messages after processing
|
||||
#[default]
|
||||
Manual,
|
||||
}
|
||||
|
||||
impl Default for AckMode {
|
||||
fn default() -> Self {
|
||||
Self::Manual
|
||||
}
|
||||
}
|
||||
|
||||
/// Exchange type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ExchangeType {
|
||||
/// Direct exchange - routes messages with exact routing key match
|
||||
#[default]
|
||||
Direct,
|
||||
/// Topic exchange - routes messages using pattern matching
|
||||
Topic,
|
||||
@@ -165,12 +156,6 @@ impl ExchangeType {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExchangeType {
|
||||
fn default() -> Self {
|
||||
Self::Direct
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExchangeType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
|
||||
@@ -41,7 +41,7 @@ impl PackEnvironmentStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
pub fn parse_status(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"pending" => Some(Self::Pending),
|
||||
"installing" => Some(Self::Installing),
|
||||
@@ -194,7 +194,7 @@ impl PackEnvironmentManager {
|
||||
|
||||
if let Some(row) = row {
|
||||
let status_str: String = row.try_get("status")?;
|
||||
let status = PackEnvironmentStatus::from_str(&status_str)
|
||||
let status = PackEnvironmentStatus::parse_status(&status_str)
|
||||
.unwrap_or(PackEnvironmentStatus::Failed);
|
||||
|
||||
Ok(Some(PackEnvironment {
|
||||
@@ -343,7 +343,7 @@ impl PackEnvironmentManager {
|
||||
let mut environments = Vec::new();
|
||||
for row in rows {
|
||||
let status_str: String = row.try_get("status")?;
|
||||
let status = PackEnvironmentStatus::from_str(&status_str)
|
||||
let status = PackEnvironmentStatus::parse_status(&status_str)
|
||||
.unwrap_or(PackEnvironmentStatus::Failed);
|
||||
|
||||
environments.push(PackEnvironment {
|
||||
@@ -452,8 +452,8 @@ impl PackEnvironmentManager {
|
||||
.await?;
|
||||
|
||||
let status_str: String = row.try_get("status")?;
|
||||
let status =
|
||||
PackEnvironmentStatus::from_str(&status_str).unwrap_or(PackEnvironmentStatus::Pending);
|
||||
let status = PackEnvironmentStatus::parse_status(&status_str)
|
||||
.unwrap_or(PackEnvironmentStatus::Pending);
|
||||
|
||||
Ok(PackEnvironment {
|
||||
id: row.try_get("id")?,
|
||||
@@ -496,8 +496,8 @@ impl PackEnvironmentManager {
|
||||
.await?;
|
||||
|
||||
let status_str: String = row.try_get("status")?;
|
||||
let status =
|
||||
PackEnvironmentStatus::from_str(&status_str).unwrap_or(PackEnvironmentStatus::Ready);
|
||||
let status = PackEnvironmentStatus::parse_status(&status_str)
|
||||
.unwrap_or(PackEnvironmentStatus::Ready);
|
||||
|
||||
Ok(PackEnvironment {
|
||||
id: row.try_get("id")?,
|
||||
@@ -580,7 +580,7 @@ impl PackEnvironmentManager {
|
||||
Ok(output) => {
|
||||
install_log.push_str(&format!("\n=== {} ===\n", action.name));
|
||||
install_log.push_str(&output);
|
||||
install_log.push_str("\n");
|
||||
install_log.push('\n');
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = format!("Installer '{}' failed: {}", action.name, e);
|
||||
@@ -701,20 +701,18 @@ impl PackEnvironmentManager {
|
||||
.map(|obj| {
|
||||
obj.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
v.as_str()
|
||||
.map(|s| {
|
||||
let resolved = self
|
||||
.resolve_template(
|
||||
s,
|
||||
pack_ref,
|
||||
runtime_ref,
|
||||
env_path,
|
||||
&pack_path_str,
|
||||
)
|
||||
.ok()?;
|
||||
Some((k.clone(), resolved))
|
||||
})
|
||||
.flatten()
|
||||
v.as_str().and_then(|s| {
|
||||
let resolved = self
|
||||
.resolve_template(
|
||||
s,
|
||||
pack_ref,
|
||||
runtime_ref,
|
||||
env_path,
|
||||
&pack_path_str,
|
||||
)
|
||||
.ok()?;
|
||||
Some((k.clone(), resolved))
|
||||
})
|
||||
})
|
||||
.collect::<HashMap<String, String>>()
|
||||
})
|
||||
@@ -877,9 +875,9 @@ mod tests {
|
||||
fn test_environment_status_conversion() {
|
||||
assert_eq!(PackEnvironmentStatus::Ready.as_str(), "ready");
|
||||
assert_eq!(
|
||||
PackEnvironmentStatus::from_str("ready"),
|
||||
PackEnvironmentStatus::parse_status("ready"),
|
||||
Some(PackEnvironmentStatus::Ready)
|
||||
);
|
||||
assert_eq!(PackEnvironmentStatus::from_str("invalid"), None);
|
||||
assert_eq!(PackEnvironmentStatus::parse_status("invalid"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,17 +151,15 @@ impl DependencyValidator {
|
||||
};
|
||||
|
||||
let error = if !satisfied {
|
||||
if detected_version.is_none() {
|
||||
Some(format!("Runtime '{}' not found on system", runtime))
|
||||
} else if let Some(ref constraint) = version_constraint {
|
||||
Some(format!(
|
||||
"Runtime '{}' version {} does not satisfy constraint '{}'",
|
||||
runtime,
|
||||
detected_version.as_ref().unwrap(),
|
||||
constraint
|
||||
))
|
||||
if let Some(ref detected) = detected_version {
|
||||
version_constraint.as_ref().map(|constraint| {
|
||||
format!(
|
||||
"Runtime '{}' version {} does not satisfy constraint '{}'",
|
||||
runtime, detected, constraint
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
Some(format!("Runtime '{}' not found on system", runtime))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -192,15 +190,13 @@ impl DependencyValidator {
|
||||
};
|
||||
|
||||
let error = if !satisfied {
|
||||
if installed_version.is_none() {
|
||||
Some(format!("Required pack '{}' is not installed", pack_ref))
|
||||
} else {
|
||||
if let Some(ref installed) = installed_version {
|
||||
Some(format!(
|
||||
"Pack '{}' version {} does not satisfy constraint '{}'",
|
||||
pack_ref,
|
||||
installed_version.as_ref().unwrap(),
|
||||
version_constraint
|
||||
pack_ref, installed, version_constraint
|
||||
))
|
||||
} else {
|
||||
Some(format!("Required pack '{}' is not installed", pack_ref))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -335,30 +331,30 @@ fn match_version_constraint(version: &str, constraint: &str) -> Result<bool> {
|
||||
}
|
||||
|
||||
// Parse constraint
|
||||
if constraint.starts_with(">=") {
|
||||
let required = constraint[2..].trim();
|
||||
if let Some(stripped) = constraint.strip_prefix(">=") {
|
||||
let required = stripped.trim();
|
||||
Ok(compare_versions(version, required)? >= 0)
|
||||
} else if constraint.starts_with("<=") {
|
||||
let required = constraint[2..].trim();
|
||||
} else if let Some(stripped) = constraint.strip_prefix("<=") {
|
||||
let required = stripped.trim();
|
||||
Ok(compare_versions(version, required)? <= 0)
|
||||
} else if constraint.starts_with('>') {
|
||||
let required = constraint[1..].trim();
|
||||
} else if let Some(stripped) = constraint.strip_prefix('>') {
|
||||
let required = stripped.trim();
|
||||
Ok(compare_versions(version, required)? > 0)
|
||||
} else if constraint.starts_with('<') {
|
||||
let required = constraint[1..].trim();
|
||||
} else if let Some(stripped) = constraint.strip_prefix('<') {
|
||||
let required = stripped.trim();
|
||||
Ok(compare_versions(version, required)? < 0)
|
||||
} else if constraint.starts_with('=') {
|
||||
let required = constraint[1..].trim();
|
||||
} else if let Some(stripped) = constraint.strip_prefix('=') {
|
||||
let required = stripped.trim();
|
||||
Ok(compare_versions(version, required)? == 0)
|
||||
} else if constraint.starts_with('^') {
|
||||
} else if let Some(stripped) = constraint.strip_prefix('^') {
|
||||
// Caret: Compatible with version (major.minor.patch)
|
||||
// ^1.2.3 := >=1.2.3 <2.0.0
|
||||
let required = constraint[1..].trim();
|
||||
let required = stripped.trim();
|
||||
match_caret_constraint(version, required)
|
||||
} else if constraint.starts_with('~') {
|
||||
} else if let Some(stripped) = constraint.strip_prefix('~') {
|
||||
// Tilde: Approximately equivalent to version
|
||||
// ~1.2.3 := >=1.2.3 <1.3.0
|
||||
let required = constraint[1..].trim();
|
||||
let required = stripped.trim();
|
||||
match_tilde_constraint(version, required)
|
||||
} else {
|
||||
// Exact match
|
||||
|
||||
@@ -171,7 +171,7 @@ impl PackInstaller {
|
||||
clone_cmd.arg("--depth").arg("1");
|
||||
}
|
||||
|
||||
clone_cmd.arg(&url).arg(&install_dir);
|
||||
clone_cmd.arg(url).arg(&install_dir);
|
||||
|
||||
let output = clone_cmd
|
||||
.output()
|
||||
@@ -421,7 +421,11 @@ impl PackInstaller {
|
||||
}
|
||||
|
||||
// Determine filename from URL
|
||||
let filename = url.split('/').last().unwrap_or("archive.zip").to_string();
|
||||
let filename = url
|
||||
.split('/')
|
||||
.next_back()
|
||||
.unwrap_or("archive.zip")
|
||||
.to_string();
|
||||
|
||||
let archive_path = self.temp_dir.join(&filename);
|
||||
|
||||
|
||||
@@ -288,17 +288,15 @@ impl<'a> PackComponentLoader<'a> {
|
||||
}
|
||||
Err(e) => {
|
||||
// Check for unique constraint violation (race condition)
|
||||
if let Error::Database(ref db_err) = e {
|
||||
if let sqlx::Error::Database(ref inner) = db_err {
|
||||
if inner.is_unique_violation() {
|
||||
info!(
|
||||
"Runtime '{}' already exists (concurrent creation), treating as update",
|
||||
runtime_ref
|
||||
);
|
||||
loaded_refs.push(runtime_ref);
|
||||
result.runtimes_updated += 1;
|
||||
continue;
|
||||
}
|
||||
if let Error::Database(sqlx::Error::Database(ref inner)) = e {
|
||||
if inner.is_unique_violation() {
|
||||
info!(
|
||||
"Runtime '{}' already exists (concurrent creation), treating as update",
|
||||
runtime_ref
|
||||
);
|
||||
loaded_refs.push(runtime_ref);
|
||||
result.runtimes_updated += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let msg = format!("Failed to create runtime '{}': {}", runtime_ref, e);
|
||||
@@ -438,16 +436,14 @@ impl<'a> PackComponentLoader<'a> {
|
||||
}
|
||||
Err(e) => {
|
||||
// Check for unique constraint violation (race condition)
|
||||
if let Error::Database(ref db_err) = e {
|
||||
if let sqlx::Error::Database(ref inner) = db_err {
|
||||
if inner.is_unique_violation() {
|
||||
info!(
|
||||
"Version '{}' for runtime '{}' already exists (concurrent), skipping",
|
||||
version_str, runtime_ref
|
||||
);
|
||||
loaded_versions.push(version_str);
|
||||
continue;
|
||||
}
|
||||
if let Error::Database(sqlx::Error::Database(ref inner)) = e {
|
||||
if inner.is_unique_violation() {
|
||||
info!(
|
||||
"Version '{}' for runtime '{}' already exists (concurrent), skipping",
|
||||
version_str, runtime_ref
|
||||
);
|
||||
loaded_versions.push(version_str);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let msg = format!(
|
||||
|
||||
@@ -272,11 +272,6 @@ impl Checksum {
|
||||
|
||||
Ok(Self { algorithm, hash })
|
||||
}
|
||||
|
||||
/// Format as "algorithm:hash"
|
||||
pub fn to_string(&self) -> String {
|
||||
format!("{}:{}", self.algorithm, self.hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Checksum {
|
||||
|
||||
@@ -295,7 +295,7 @@ impl Update for ActionRepository {
|
||||
|
||||
query.push(", updated = NOW() WHERE id = ");
|
||||
query.push_bind(id);
|
||||
query.push(&format!(" RETURNING {}", ACTION_COLUMNS));
|
||||
query.push(format!(" RETURNING {}", ACTION_COLUMNS));
|
||||
|
||||
let action = query
|
||||
.build_query_as::<Action>()
|
||||
@@ -554,7 +554,6 @@ impl ActionRepository {
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository for Policy operations
|
||||
// ============================================================================
|
||||
// Policy Repository
|
||||
// ============================================================================
|
||||
|
||||
@@ -47,7 +47,7 @@ pub struct HistoryQueryParams {
|
||||
impl HistoryQueryParams {
|
||||
/// Returns the effective limit, capped at 1000.
|
||||
pub fn effective_limit(&self) -> i64 {
|
||||
self.limit.unwrap_or(100).min(1000).max(1)
|
||||
self.limit.unwrap_or(100).clamp(1, 1000)
|
||||
}
|
||||
|
||||
/// Returns the effective offset.
|
||||
|
||||
@@ -582,7 +582,7 @@ impl EnforcementRepository {
|
||||
}
|
||||
|
||||
if let Some(status) = &filters.status {
|
||||
push_condition!("status = ", status.clone());
|
||||
push_condition!("status = ", *status);
|
||||
}
|
||||
if let Some(rule_id) = filters.rule {
|
||||
push_condition!("rule = ", rule_id);
|
||||
|
||||
@@ -391,7 +391,7 @@ impl ExecutionRepository {
|
||||
}
|
||||
|
||||
if let Some(status) = &filters.status {
|
||||
push_condition!("e.status = ", status.clone());
|
||||
push_condition!("e.status = ", *status);
|
||||
}
|
||||
if let Some(action_ref) = &filters.action_ref {
|
||||
push_condition!("e.action_ref = ", action_ref.clone());
|
||||
|
||||
@@ -129,7 +129,7 @@ impl Update for IdentityRepository {
|
||||
.map_err(|e| {
|
||||
// Convert RowNotFound to NotFound error
|
||||
if matches!(e, sqlx::Error::RowNotFound) {
|
||||
return crate::Error::not_found("identity", "id", &id.to_string());
|
||||
return crate::Error::not_found("identity", "id", id.to_string());
|
||||
}
|
||||
e.into()
|
||||
})
|
||||
|
||||
@@ -211,7 +211,7 @@ impl InquiryRepository {
|
||||
}
|
||||
|
||||
if let Some(status) = &filters.status {
|
||||
push_condition!("status = ", status.clone());
|
||||
push_condition!("status = ", *status);
|
||||
}
|
||||
if let Some(execution_id) = filters.execution {
|
||||
push_condition!("execution = ", execution_id);
|
||||
|
||||
@@ -218,7 +218,7 @@ impl KeyRepository {
|
||||
}
|
||||
|
||||
if let Some(ref owner_type) = filters.owner_type {
|
||||
push_condition!("owner_type = ", owner_type.clone());
|
||||
push_condition!("owner_type = ", *owner_type);
|
||||
}
|
||||
if let Some(ref owner) = filters.owner {
|
||||
push_condition!("owner = ", owner.clone());
|
||||
|
||||
@@ -408,6 +408,7 @@ impl PackRepository {
|
||||
}
|
||||
|
||||
/// Update installation metadata for a pack
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn update_installation_metadata<'e, E>(
|
||||
executor: E,
|
||||
id: i64,
|
||||
|
||||
@@ -239,7 +239,7 @@ impl Update for RuntimeVersionRepository {
|
||||
|
||||
query.push(" WHERE id = ");
|
||||
query.push_bind(id);
|
||||
query.push(&format!(" RETURNING {}", SELECT_COLUMNS));
|
||||
query.push(format!(" RETURNING {}", SELECT_COLUMNS));
|
||||
|
||||
let row = query
|
||||
.build_query_as::<RuntimeVersion>()
|
||||
|
||||
@@ -276,7 +276,7 @@ impl Update for TriggerRepository {
|
||||
.map_err(|e| {
|
||||
// Convert RowNotFound to NotFound error
|
||||
if matches!(e, sqlx::Error::RowNotFound) {
|
||||
return crate::Error::not_found("trigger", "id", &id.to_string());
|
||||
return crate::Error::not_found("trigger", "id", id.to_string());
|
||||
}
|
||||
e.into()
|
||||
})?;
|
||||
|
||||
@@ -423,14 +423,11 @@ mod tests {
|
||||
"always_available": true
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
verification
|
||||
.get("always_available")
|
||||
.unwrap()
|
||||
.as_bool()
|
||||
.unwrap(),
|
||||
true
|
||||
);
|
||||
assert!(verification
|
||||
.get("always_available")
|
||||
.unwrap()
|
||||
.as_bool()
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -106,7 +106,7 @@ impl TestExecutor {
|
||||
);
|
||||
|
||||
match self
|
||||
.execute_test_suite(&pack_dir, runner_name, runner_config)
|
||||
.execute_test_suite(pack_dir, runner_name, runner_config)
|
||||
.await
|
||||
{
|
||||
Ok(suite_result) => {
|
||||
@@ -369,7 +369,7 @@ impl TestExecutor {
|
||||
let total = self.extract_number(&text, "Total Tests:");
|
||||
let passed = self.extract_number(&text, "Passed:");
|
||||
let failed = self.extract_number(&text, "Failed:");
|
||||
let skipped = self.extract_number(&text, "Skipped:").or_else(|| Some(0));
|
||||
let skipped = self.extract_number(&text, "Skipped:").or(Some(0));
|
||||
|
||||
// If we couldn't parse counts, use exit code
|
||||
let (total, passed, failed, skipped) = if total.is_none() || passed.is_none() {
|
||||
@@ -441,7 +441,6 @@ impl TestExecutor {
|
||||
.and_then(|line| {
|
||||
line.split(label)
|
||||
.nth(1)?
|
||||
.trim()
|
||||
.split_whitespace()
|
||||
.next()?
|
||||
.parse::<i32>()
|
||||
|
||||
@@ -278,7 +278,7 @@ fn json_eq(a: &JsonValue, b: &JsonValue) -> bool {
|
||||
return false;
|
||||
}
|
||||
a.iter()
|
||||
.all(|(k, v)| b.get(k).map_or(false, |bv| json_eq(v, bv)))
|
||||
.all(|(k, v)| b.get(k).is_some_and(|bv| json_eq(v, bv)))
|
||||
}
|
||||
// Different types (other than number cross-compare) are never equal
|
||||
_ => false,
|
||||
@@ -680,10 +680,8 @@ impl NumericValue {
|
||||
fn as_numeric(v: &JsonValue) -> Option<NumericValue> {
|
||||
if let Some(i) = v.as_i64() {
|
||||
Some(NumericValue::Int(i))
|
||||
} else if let Some(f) = v.as_f64() {
|
||||
Some(NumericValue::Float(f))
|
||||
} else {
|
||||
None
|
||||
v.as_f64().map(NumericValue::Float)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -363,8 +363,8 @@ mod tests {
|
||||
let ctx = TestContext::new();
|
||||
assert_eq!(eval_expression("string(42)", &ctx).unwrap(), json!("42"));
|
||||
assert_eq!(
|
||||
eval_expression("number(\"3.14\")", &ctx).unwrap(),
|
||||
json!(3.14)
|
||||
eval_expression("number(\"3.15\")", &ctx).unwrap(),
|
||||
json!(3.15)
|
||||
);
|
||||
assert_eq!(eval_expression("int(3.9)", &ctx).unwrap(), json!(3));
|
||||
assert_eq!(eval_expression("int(\"42\")", &ctx).unwrap(), json!(42));
|
||||
|
||||
@@ -431,8 +431,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_float() {
|
||||
let kinds = tokenize("3.14");
|
||||
assert_eq!(kinds, vec![TokenKind::Float(3.14), TokenKind::Eof]);
|
||||
let kinds = tokenize("3.15");
|
||||
assert_eq!(kinds, vec![TokenKind::Float(3.15), TokenKind::Eof]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -187,11 +187,13 @@ impl WorkflowLoader {
|
||||
.map(|e| e.to_string())
|
||||
};
|
||||
|
||||
if validation_error.is_some() && !self.config.skip_validation {
|
||||
return Err(Error::validation(format!(
|
||||
"Workflow validation failed: {}",
|
||||
validation_error.as_ref().unwrap()
|
||||
)));
|
||||
if let Some(ref err) = validation_error {
|
||||
if !self.config.skip_validation {
|
||||
return Err(Error::validation(format!(
|
||||
"Workflow validation failed: {}",
|
||||
err
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LoadedWorkflow {
|
||||
|
||||
@@ -1449,7 +1449,7 @@ tasks:
|
||||
publish:
|
||||
- validation_passed: true
|
||||
- count: 42
|
||||
- ratio: 3.14
|
||||
- ratio: 3.15
|
||||
- label: "hello"
|
||||
- template_val: "{{ result().data }}"
|
||||
- nothing: null
|
||||
@@ -1486,7 +1486,7 @@ tasks:
|
||||
} else if let Some(val) = map.get("count") {
|
||||
assert_eq!(val, &serde_json::json!(42), "integer");
|
||||
} else if let Some(val) = map.get("ratio") {
|
||||
assert_eq!(val, &serde_json::json!(3.14), "float");
|
||||
assert_eq!(val, &serde_json::json!(3.15), "float");
|
||||
} else if let Some(val) = map.get("label") {
|
||||
assert_eq!(val, &serde_json::json!("hello"), "string");
|
||||
} else if let Some(val) = map.get("template_val") {
|
||||
|
||||
@@ -97,11 +97,11 @@ impl WorkflowRegistrar {
|
||||
debug!("Registering workflow: {}", loaded.file.ref_name);
|
||||
|
||||
// Check for validation errors
|
||||
if loaded.validation_error.is_some() {
|
||||
if let Some(ref err) = loaded.validation_error {
|
||||
if self.options.skip_invalid {
|
||||
return Err(Error::validation(format!(
|
||||
"Workflow has validation errors: {}",
|
||||
loaded.validation_error.as_ref().unwrap()
|
||||
err
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,7 @@ impl WorkflowRegistrar {
|
||||
///
|
||||
/// `effective_ref` and `effective_label` are the resolved values (which may
|
||||
/// have been derived from the filename when the workflow YAML omits them).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn create_companion_action(
|
||||
&self,
|
||||
workflow_def_id: i64,
|
||||
@@ -298,6 +299,7 @@ impl WorkflowRegistrar {
|
||||
///
|
||||
/// `effective_ref` and `effective_label` are the resolved values (which may
|
||||
/// have been derived from the filename when the workflow YAML omits them).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn ensure_companion_action(
|
||||
&self,
|
||||
workflow_def_id: i64,
|
||||
@@ -425,8 +427,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_registration_options_default() {
|
||||
let options = RegistrationOptions::default();
|
||||
assert_eq!(options.update_existing, true);
|
||||
assert_eq!(options.skip_invalid, true);
|
||||
assert!(options.update_existing);
|
||||
assert!(options.skip_invalid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -439,7 +441,7 @@ mod tests {
|
||||
};
|
||||
|
||||
assert_eq!(result.ref_name, "test.workflow");
|
||||
assert_eq!(result.created, true);
|
||||
assert!(result.created);
|
||||
assert_eq!(result.workflow_def_id, 123);
|
||||
assert_eq!(result.warnings.len(), 0);
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ impl WorkflowValidator {
|
||||
reachable
|
||||
}
|
||||
|
||||
/// Detect cycles using DFS
|
||||
// Detect cycles using DFS
|
||||
// Cycle detection removed - cycles are now valid in workflow graphs
|
||||
// Workflows are directed graphs (not DAGs) and cycles are supported
|
||||
// for use cases like monitoring loops, retry patterns, etc.
|
||||
@@ -328,7 +328,7 @@ impl WorkflowValidator {
|
||||
}
|
||||
|
||||
// Validate variable names in vars
|
||||
for (key, _) in &workflow.vars {
|
||||
for key in workflow.vars.keys() {
|
||||
if !Self::is_valid_variable_name(key) {
|
||||
return Err(ValidationError::SemanticError(format!(
|
||||
"Invalid variable name: {}",
|
||||
|
||||
@@ -781,7 +781,7 @@ async fn test_find_executions_by_enforcement() {
|
||||
config: None,
|
||||
env_vars: None,
|
||||
parent: None,
|
||||
enforcement: if i == 2 { None } else { None }, // Can't reference non-existent enforcement
|
||||
enforcement: None, // Can't reference non-existent enforcement
|
||||
executor: None,
|
||||
status: ExecutionStatus::Requested,
|
||||
result: None,
|
||||
|
||||
@@ -35,7 +35,7 @@ async fn test_create_key_system_owner() {
|
||||
assert_eq!(key.owner_pack, None);
|
||||
assert_eq!(key.owner_action, None);
|
||||
assert_eq!(key.owner_sensor, None);
|
||||
assert_eq!(key.encrypted, false);
|
||||
assert!(!key.encrypted);
|
||||
assert_eq!(key.value, "test_value");
|
||||
assert!(key.created.timestamp() > 0);
|
||||
assert!(key.updated.timestamp() > 0);
|
||||
@@ -52,7 +52,7 @@ async fn test_create_key_system_encrypted() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, true);
|
||||
assert!(key.encrypted);
|
||||
assert_eq!(key.encryption_key_hash, Some("sha256:abc123".to_string()));
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ async fn test_update_encrypted_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, false);
|
||||
assert!(!key.encrypted);
|
||||
|
||||
let input = UpdateKeyInput {
|
||||
encrypted: Some(true),
|
||||
@@ -438,7 +438,7 @@ async fn test_update_encrypted_status() {
|
||||
|
||||
let updated = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(updated.encrypted, true);
|
||||
assert!(updated.encrypted);
|
||||
assert_eq!(
|
||||
updated.encryption_key_hash,
|
||||
Some("sha256:xyz789".to_string())
|
||||
@@ -468,7 +468,7 @@ async fn test_update_multiple_fields() {
|
||||
|
||||
assert_eq!(updated.name, new_name);
|
||||
assert_eq!(updated.value, "updated_value");
|
||||
assert_eq!(updated.encrypted, true);
|
||||
assert!(updated.encrypted);
|
||||
assert_eq!(updated.encryption_key_hash, Some("hash123".to_string()));
|
||||
}
|
||||
|
||||
@@ -768,10 +768,10 @@ async fn test_key_encrypted_flag() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain_key.encrypted, false);
|
||||
assert!(!plain_key.encrypted);
|
||||
assert_eq!(plain_key.encryption_key_hash, None);
|
||||
|
||||
assert_eq!(encrypted_key.encrypted, true);
|
||||
assert!(encrypted_key.encrypted);
|
||||
assert_eq!(
|
||||
encrypted_key.encryption_key_hash,
|
||||
Some("sha256:abc".to_string())
|
||||
@@ -788,7 +788,7 @@ async fn test_update_encryption_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(key.encrypted, false);
|
||||
assert!(!key.encrypted);
|
||||
|
||||
// Encrypt it
|
||||
let input = UpdateKeyInput {
|
||||
@@ -800,7 +800,7 @@ async fn test_update_encryption_status() {
|
||||
|
||||
let encrypted = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(encrypted.encrypted, true);
|
||||
assert!(encrypted.encrypted);
|
||||
assert_eq!(
|
||||
encrypted.encryption_key_hash,
|
||||
Some("sha256:newkey".to_string())
|
||||
@@ -817,7 +817,7 @@ async fn test_update_encryption_status() {
|
||||
|
||||
let decrypted = KeyRepository::update(&pool, key.id, input).await.unwrap();
|
||||
|
||||
assert_eq!(decrypted.encrypted, false);
|
||||
assert!(!decrypted.encrypted);
|
||||
assert_eq!(decrypted.value, "plain_value");
|
||||
}
|
||||
|
||||
|
||||
@@ -892,7 +892,7 @@ async fn test_port_range() {
|
||||
|
||||
let worker = WorkerRepository::create(&pool, input)
|
||||
.await
|
||||
.expect(&format!("Failed to create worker with port {}", port));
|
||||
.unwrap_or_else(|_| panic!("Failed to create worker with port {}", port));
|
||||
|
||||
assert_eq!(worker.port, Some(port));
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ async fn test_create_rule() {
|
||||
rule.conditions,
|
||||
json!({"equals": {"event.status": "success"}})
|
||||
);
|
||||
assert_eq!(rule.enabled, true);
|
||||
assert!(rule.enabled);
|
||||
assert!(rule.created.timestamp() > 0);
|
||||
assert!(rule.updated.timestamp() > 0);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ async fn test_create_rule_disabled() {
|
||||
|
||||
let rule = RuleRepository::create(&pool, input).await.unwrap();
|
||||
|
||||
assert_eq!(rule.enabled, false);
|
||||
assert!(!rule.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -759,7 +759,7 @@ async fn test_update_rule_enabled() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated.enabled, false);
|
||||
assert!(!updated.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -816,7 +816,7 @@ async fn test_update_rule_multiple_fields() {
|
||||
assert_eq!(updated.label, "New Label");
|
||||
assert_eq!(updated.description, "New Description");
|
||||
assert_eq!(updated.conditions, json!({"updated": true}));
|
||||
assert_eq!(updated.enabled, false);
|
||||
assert!(!updated.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -61,7 +61,7 @@ async fn test_create_sensor_minimal() {
|
||||
assert_eq!(sensor.runtime_ref, runtime.r#ref);
|
||||
assert_eq!(sensor.trigger, trigger.id);
|
||||
assert_eq!(sensor.trigger_ref, trigger.r#ref);
|
||||
assert_eq!(sensor.enabled, true);
|
||||
assert!(sensor.enabled);
|
||||
assert_eq!(sensor.param_schema, None);
|
||||
assert!(sensor.created.timestamp() > 0);
|
||||
assert!(sensor.updated.timestamp() > 0);
|
||||
@@ -796,7 +796,7 @@ async fn test_update_enabled_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(sensor.enabled, true);
|
||||
assert!(sensor.enabled);
|
||||
|
||||
let input = UpdateSensorInput {
|
||||
enabled: Some(false),
|
||||
@@ -807,7 +807,7 @@ async fn test_update_enabled_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated.enabled, false);
|
||||
assert!(!updated.enabled);
|
||||
|
||||
// Enable it again
|
||||
let input = UpdateSensorInput {
|
||||
@@ -819,7 +819,7 @@ async fn test_update_enabled_status() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated.enabled, true);
|
||||
assert!(updated.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -924,7 +924,7 @@ async fn test_update_multiple_fields() {
|
||||
assert_eq!(updated.label, "Multi Update");
|
||||
assert_eq!(updated.description, "Updated multiple fields");
|
||||
assert_eq!(updated.entrypoint, "sensors/multi.py");
|
||||
assert_eq!(updated.enabled, false);
|
||||
assert!(!updated.enabled);
|
||||
assert_eq!(updated.param_schema, Some(json!({"type": "object"})));
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ async fn test_create_trigger() {
|
||||
assert_eq!(trigger.pack, Some(pack.id));
|
||||
assert_eq!(trigger.pack_ref, Some(pack.r#ref));
|
||||
assert_eq!(trigger.label, "Webhook Trigger");
|
||||
assert_eq!(trigger.enabled, true);
|
||||
assert!(trigger.enabled);
|
||||
assert!(trigger.created.timestamp() > 0);
|
||||
assert!(trigger.updated.timestamp() > 0);
|
||||
}
|
||||
@@ -134,7 +134,7 @@ async fn test_create_trigger_disabled() {
|
||||
|
||||
let trigger = TriggerRepository::create(&pool, input).await.unwrap();
|
||||
|
||||
assert_eq!(trigger.enabled, false);
|
||||
assert!(!trigger.enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -478,7 +478,7 @@ async fn test_update_trigger() {
|
||||
assert_eq!(updated.r#ref, trigger.r#ref); // Ref should not change
|
||||
assert_eq!(updated.label, "Updated Label");
|
||||
assert_eq!(updated.description, Some("Updated description".to_string()));
|
||||
assert_eq!(updated.enabled, false);
|
||||
assert!(!updated.enabled);
|
||||
assert!(updated.updated > original_updated);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user