re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

381
crates/api/src/dto/pack.rs Normal file
View File

@@ -0,0 +1,381 @@
//! Pack DTOs for API requests and responses
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use utoipa::ToSchema;
use validator::Validate;
/// Request DTO for creating a new pack
#[derive(Debug, Clone, Deserialize, Validate, ToSchema)]
pub struct CreatePackRequest {
/// Unique reference identifier (e.g., "core", "aws", "slack")
#[validate(length(min = 1, max = 255))]
#[schema(example = "slack")]
pub r#ref: String,
/// Human-readable label
#[validate(length(min = 1, max = 255))]
#[schema(example = "Slack Integration")]
pub label: String,
/// Pack description
#[schema(example = "Integration with Slack for messaging and notifications")]
pub description: Option<String>,
/// Pack version (semver format recommended)
#[validate(length(min = 1, max = 50))]
#[schema(example = "1.0.0")]
pub version: String,
/// Configuration schema (JSON Schema)
#[serde(default = "default_empty_object")]
#[schema(value_type = Object, example = json!({"type": "object", "properties": {"api_token": {"type": "string"}}}))]
pub conf_schema: JsonValue,
/// Pack configuration values
#[serde(default = "default_empty_object")]
#[schema(value_type = Object, example = json!({"api_token": "xoxb-..."}))]
pub config: JsonValue,
/// Pack metadata
#[serde(default = "default_empty_object")]
#[schema(value_type = Object, example = json!({"author": "Attune Team"}))]
pub meta: JsonValue,
/// Tags for categorization
#[serde(default)]
#[schema(example = json!(["messaging", "collaboration"]))]
pub tags: Vec<String>,
/// Runtime dependencies (refs of required packs)
#[serde(default)]
#[schema(example = json!(["core"]))]
pub runtime_deps: Vec<String>,
/// Whether this is a standard/built-in pack
#[serde(default)]
#[schema(example = false)]
pub is_standard: bool,
}
/// Request DTO for registering a pack from local filesystem
#[derive(Debug, Clone, Deserialize, Validate, ToSchema)]
pub struct RegisterPackRequest {
/// Local filesystem path to the pack directory
#[validate(length(min = 1))]
#[schema(example = "/path/to/packs/mypack")]
pub path: String,
/// Skip running pack tests during registration
#[serde(default)]
#[schema(example = false)]
pub skip_tests: bool,
/// Force registration even if tests fail
#[serde(default)]
#[schema(example = false)]
pub force: bool,
}
/// Request DTO for installing a pack from remote source
#[derive(Debug, Clone, Deserialize, Validate, ToSchema)]
pub struct InstallPackRequest {
/// Repository URL or source location
#[validate(length(min = 1))]
#[schema(example = "https://github.com/attune/pack-slack.git")]
pub source: String,
/// Git branch, tag, or commit reference
#[schema(example = "main")]
pub ref_spec: Option<String>,
/// Force reinstall if pack already exists
#[serde(default)]
#[schema(example = false)]
pub force: bool,
/// Skip running pack tests during installation
#[serde(default)]
#[schema(example = false)]
pub skip_tests: bool,
/// Skip dependency validation (not recommended)
#[serde(default)]
#[schema(example = false)]
pub skip_deps: bool,
}
/// Response for pack install/register operations with test results
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PackInstallResponse {
/// The installed/registered pack
pub pack: PackResponse,
/// Test execution result (if tests were run)
pub test_result: Option<attune_common::models::pack_test::PackTestResult>,
/// Whether tests were skipped
pub tests_skipped: bool,
}
/// Request DTO for updating a pack
#[derive(Debug, Clone, Deserialize, Validate, ToSchema)]
pub struct UpdatePackRequest {
/// Human-readable label
#[validate(length(min = 1, max = 255))]
#[schema(example = "Slack Integration v2")]
pub label: Option<String>,
/// Pack description
#[schema(example = "Enhanced Slack integration with new features")]
pub description: Option<String>,
/// Pack version
#[validate(length(min = 1, max = 50))]
#[schema(example = "2.0.0")]
pub version: Option<String>,
/// Configuration schema
#[schema(value_type = Object, nullable = true)]
pub conf_schema: Option<JsonValue>,
/// Pack configuration values
#[schema(value_type = Object, nullable = true)]
pub config: Option<JsonValue>,
/// Pack metadata
#[schema(value_type = Object, nullable = true)]
pub meta: Option<JsonValue>,
/// Tags for categorization
#[schema(example = json!(["messaging", "collaboration", "webhooks"]))]
pub tags: Option<Vec<String>>,
/// Runtime dependencies
#[schema(example = json!(["core", "http"]))]
pub runtime_deps: Option<Vec<String>>,
/// Whether this is a standard pack
#[schema(example = false)]
pub is_standard: Option<bool>,
}
/// Response DTO for pack information
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PackResponse {
/// Pack ID
#[schema(example = 1)]
pub id: i64,
/// Unique reference identifier
#[schema(example = "slack")]
pub r#ref: String,
/// Human-readable label
#[schema(example = "Slack Integration")]
pub label: String,
/// Pack description
#[schema(example = "Integration with Slack for messaging and notifications")]
pub description: Option<String>,
/// Pack version
#[schema(example = "1.0.0")]
pub version: String,
/// Configuration schema
#[schema(value_type = Object)]
pub conf_schema: JsonValue,
/// Pack configuration
#[schema(value_type = Object)]
pub config: JsonValue,
/// Pack metadata
#[schema(value_type = Object)]
pub meta: JsonValue,
/// Tags
#[schema(example = json!(["messaging", "collaboration"]))]
pub tags: Vec<String>,
/// Runtime dependencies
#[schema(example = json!(["core"]))]
pub runtime_deps: Vec<String>,
/// Is standard pack
#[schema(example = false)]
pub is_standard: bool,
/// Creation timestamp
#[schema(example = "2024-01-13T10:30:00Z")]
pub created: DateTime<Utc>,
/// Last update timestamp
#[schema(example = "2024-01-13T10:30:00Z")]
pub updated: DateTime<Utc>,
}
/// Simplified pack response (for list endpoints)
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PackSummary {
/// Pack ID
#[schema(example = 1)]
pub id: i64,
/// Unique reference identifier
#[schema(example = "slack")]
pub r#ref: String,
/// Human-readable label
#[schema(example = "Slack Integration")]
pub label: String,
/// Pack description
#[schema(example = "Integration with Slack for messaging and notifications")]
pub description: Option<String>,
/// Pack version
#[schema(example = "1.0.0")]
pub version: String,
/// Tags
#[schema(example = json!(["messaging", "collaboration"]))]
pub tags: Vec<String>,
/// Is standard pack
#[schema(example = false)]
pub is_standard: bool,
/// Creation timestamp
#[schema(example = "2024-01-13T10:30:00Z")]
pub created: DateTime<Utc>,
/// Last update timestamp
#[schema(example = "2024-01-13T10:30:00Z")]
pub updated: DateTime<Utc>,
}
/// Convert from Pack model to PackResponse
impl From<attune_common::models::Pack> for PackResponse {
fn from(pack: attune_common::models::Pack) -> Self {
Self {
id: pack.id,
r#ref: pack.r#ref,
label: pack.label,
description: pack.description,
version: pack.version,
conf_schema: pack.conf_schema,
config: pack.config,
meta: pack.meta,
tags: pack.tags,
runtime_deps: pack.runtime_deps,
is_standard: pack.is_standard,
created: pack.created,
updated: pack.updated,
}
}
}
/// Convert from Pack model to PackSummary
impl From<attune_common::models::Pack> for PackSummary {
fn from(pack: attune_common::models::Pack) -> Self {
Self {
id: pack.id,
r#ref: pack.r#ref,
label: pack.label,
description: pack.description,
version: pack.version,
tags: pack.tags,
is_standard: pack.is_standard,
created: pack.created,
updated: pack.updated,
}
}
}
/// Response for pack workflow sync operation
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PackWorkflowSyncResponse {
/// Pack reference
pub pack_ref: String,
/// Number of workflows loaded from filesystem
pub loaded_count: usize,
/// Number of workflows registered/updated in database
pub registered_count: usize,
/// Individual workflow registration results
pub workflows: Vec<WorkflowSyncResult>,
/// Any errors encountered during sync
pub errors: Vec<String>,
}
/// Individual workflow sync result
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct WorkflowSyncResult {
/// Workflow reference name
pub ref_name: String,
/// Whether the workflow was created (false = updated)
pub created: bool,
/// Workflow definition ID
pub workflow_def_id: i64,
/// Any warnings during registration
pub warnings: Vec<String>,
}
/// Response for pack workflow validation operation
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct PackWorkflowValidationResponse {
/// Pack reference
pub pack_ref: String,
/// Number of workflows validated
pub validated_count: usize,
/// Number of workflows with errors
pub error_count: usize,
/// Validation errors by workflow reference
pub errors: std::collections::HashMap<String, Vec<String>>,
}
fn default_empty_object() -> JsonValue {
serde_json::json!({})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_pack_request_defaults() {
let json = r#"{
"ref": "test-pack",
"label": "Test Pack",
"version": "1.0.0"
}"#;
let req: CreatePackRequest = serde_json::from_str(json).unwrap();
assert_eq!(req.r#ref, "test-pack");
assert_eq!(req.label, "Test Pack");
assert_eq!(req.version, "1.0.0");
assert!(req.tags.is_empty());
assert!(req.runtime_deps.is_empty());
assert!(!req.is_standard);
}
#[test]
fn test_create_pack_request_validation() {
let req = CreatePackRequest {
r#ref: "".to_string(), // Invalid: empty
label: "Test".to_string(),
version: "1.0.0".to_string(),
description: None,
conf_schema: default_empty_object(),
config: default_empty_object(),
meta: default_empty_object(),
tags: vec![],
runtime_deps: vec![],
is_standard: false,
};
assert!(req.validate().is_err());
}
}