use serde::{Serialize, Deserialize};
use async_graphql::{SimpleObject, Context, Result, Object, Enum, Union};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
#[derive(SimpleObject, Clone, Serialize, Deserialize)]
pub struct ApiVersion {
pub version: String,
pub deprecated: bool,
pub removal_date: Option<DateTime<Utc>>,
pub supported_until: Option<DateTime<Utc>>,
pub migration_guide: Option<String>,
pub features: Vec<String>,
pub breaking_changes: Vec<BreakingChange>,
pub compatibility: CompatibilityInfo,
}
#[derive(SimpleObject, Clone, Serialize, Deserialize)]
pub struct BreakingChange {
pub component: String,
pub description: String,
pub migration_required: bool,
pub affected_operations: Vec<String>,
}
#[derive(SimpleObject, Clone, Serialize, Deserialize)]
pub struct CompatibilityInfo {
pub min_client_version: String,
pub max_client_version: Option<String>,
pub protocol_version: String,
pub backward_compatible: bool,
pub forward_compatible: bool,
}
#[derive(SimpleObject, Clone)]
pub struct VersionInfo {
pub current: String,
pub supported: Vec<ApiVersion>,
pub deprecated: Vec<ApiVersion>,
pub removed: Vec<ApiVersion>,
pub default_version: String,
pub release_notes: HashMap<String, ReleaseNotes>,
}
#[derive(SimpleObject, Clone, Serialize, Deserialize)]
pub struct ReleaseNotes {
pub version: String,
pub release_date: DateTime<Utc>,
pub features: Vec<String>,
pub bug_fixes: Vec<String>,
pub security_updates: Vec<String>,
pub breaking_changes: Vec<BreakingChange>,
pub migration_notes: Option<String>,
}
#[derive(SimpleObject, Clone)]
pub struct DeprecationWarning {
pub message: String,
pub removal_date: DateTime<Utc>,
pub alternative: String,
pub migration_guide: String,
pub urgency: DeprecationUrgency,
}
#[derive(Enum, Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeprecationUrgency {
Low,
Medium,
High,
Critical,
}
#[derive(Enum, Clone, Copy, Debug, Eq, PartialEq)]
pub enum CheckStatus {
Pass,
Warning,
Fail,
}
#[derive(SimpleObject, Clone)]
pub struct MigrationScript {
pub from_version: String,
pub to_version: String,
pub steps: Vec<MigrationStep>,
pub estimated_time_secs: u64,
pub complexity: MigrationComplexity,
pub prerequisites: Vec<String>,
pub rollback_available: bool,
}
#[derive(SimpleObject, Clone)]
pub struct MigrationStep {
pub description: String,
pub code: String,
pub breaking_change: bool,
pub estimated_duration_secs: u64,
pub verification_query: Option<String>,
}
#[derive(Enum, Clone, Copy, Debug, Eq, PartialEq)]
pub enum MigrationComplexity {
Low,
Medium,
High,
Expert,
}
pub struct VersionRegistry {
versions: HashMap<String, ApiVersion>,
default_version: String,
deprecation_manager: DeprecationManager,
}
impl VersionRegistry {
pub fn new() -> Self {
let mut registry = Self {
versions: HashMap::new(),
default_version: "v2".to_string(),
deprecation_manager: DeprecationManager::new(),
};
registry.register_version(ApiVersion {
version: "v1".to_string(),
deprecated: false,
removal_date: None,
supported_until: Some(DateTime::parse_from_rfc3339("2024-12-31T23:59:59Z").unwrap().with_timezone(&Utc)),
migration_guide: Some("https://docs.fortress.security/migration/v1-to-v2".to_string()),
features: vec![
"basic_authentication".to_string(),
"simple_encryption".to_string(),
"basic_queries".to_string(),
"file_operations".to_string(),
],
breaking_changes: vec![],
compatibility: CompatibilityInfo {
min_client_version: "1.0.0".to_string(),
max_client_version: None,
protocol_version: "1.0".to_string(),
backward_compatible: true,
forward_compatible: false,
},
});
registry.register_version(ApiVersion {
version: "v2".to_string(),
deprecated: false,
removal_date: None,
supported_until: None,
migration_guide: None,
features: vec![
"enhanced_authentication".to_string(),
"field_level_encryption".to_string(),
"advanced_queries".to_string(),
"real_time_subscriptions".to_string(),
"performance_monitoring".to_string(),
"batch_operations".to_string(),
"query_optimization".to_string(),
"compliance_reporting".to_string(),
],
breaking_changes: vec![
BreakingChange {
component: "authentication".to_string(),
description: "JWT token format changed to include refresh token".to_string(),
migration_required: true,
affected_operations: vec!["login".to_string(), "token_refresh".to_string()],
},
BreakingChange {
component: "queries".to_string(),
description: "Query response format changed to include metadata".to_string(),
migration_required: false,
affected_operations: vec!["execute_query".to_string(), "batch_queries".to_string()],
},
],
compatibility: CompatibilityInfo {
min_client_version: "2.0.0".to_string(),
max_client_version: None,
protocol_version: "2.0".to_string(),
backward_compatible: false,
forward_compatible: true,
},
});
registry.register_version(ApiVersion {
version: "v3".to_string(),
deprecated: false,
removal_date: None,
supported_until: None,
migration_guide: Some("https://docs.fortress.security/migration/v2-to-v3".to_string()),
features: vec![
"quantum_resistant_crypto".to_string(),
"advanced_compliance".to_string(),
"ml_analytics".to_string(),
"edge_computing".to_string(),
"zero_knowledge_proofs".to_string(),
"blockchain_audit".to_string(),
],
breaking_changes: vec![
BreakingChange {
component: "encryption".to_string(),
description: "Default encryption algorithm changed to AEGIS256".to_string(),
migration_required: true,
affected_operations: vec!["encrypt".to_string(), "decrypt".to_string(), "key_generate".to_string()],
},
BreakingChange {
component: "compliance".to_string(),
description: "Compliance framework data structure changed".to_string(),
migration_required: true,
affected_operations: vec!["compliance_audit".to_string(), "compliance_report".to_string()],
},
],
compatibility: CompatibilityInfo {
min_client_version: "3.0.0".to_string(),
max_client_version: None,
protocol_version: "3.0".to_string(),
backward_compatible: false,
forward_compatible: true,
},
});
registry
}
pub fn register_version(&mut self, version: ApiVersion) {
self.versions.insert(version.version.clone(), version);
}
pub fn get_version(&self, version: &str) -> Option<&ApiVersion> {
self.versions.get(version)
}
pub fn is_supported(&self, version: &str) -> bool {
if let Some(api_version) = self.get_version(version) {
!api_version.deprecated && api_version.removal_date.is_none()
} else {
false
}
}
pub fn is_deprecated(&self, version: &str) -> bool {
if let Some(api_version) = self.get_version(version) {
api_version.deprecated
} else {
false
}
}
pub fn get_default_version(&self) -> &str {
&self.default_version
}
pub fn list_supported_versions(&self) -> Vec<&ApiVersion> {
self.versions.values()
.filter(|v| !v.deprecated && v.removal_date.is_none())
.collect()
}
pub fn list_deprecated_versions(&self) -> Vec<&ApiVersion> {
self.versions.values()
.filter(|v| v.deprecated)
.collect()
}
pub fn get_compatible_versions(&self, client_version: &str) -> Vec<&ApiVersion> {
self.versions.values()
.filter(|v| {
v.compatibility.backward_compatible || v.version == client_version
})
.collect()
}
pub fn get_migration_path(&self, from: &str, to: &str) -> Option<Vec<String>> {
if from == to {
return Some(vec![from.to_string()]);
}
let mut path = vec![from.to_string()];
if self.get_version(to).is_some() {
path.push(to.to_string());
return Some(path);
}
None
}
}
pub struct UserResolver;
#[Object]
impl UserResolver {
async fn user(
&self,
ctx: &Context<'_>,
id: String,
version: Option<String>
) -> Result<UserResponse> {
let version_registry = ctx.data::<VersionRegistry>()?;
let requested_version = version.unwrap_or_else(|| version_registry.get_default_version().to_string());
if !version_registry.is_supported(&requested_version) {
return Err(async_graphql::Error::new(format!(
"Version {} is not supported. Use {} or later.",
requested_version,
version_registry.get_default_version()
)));
}
match requested_version.as_str() {
"v1" => {
let user = fetch_user_v1(&id).await?;
Ok(UserResponse::V1(user))
},
"v2" => {
let user = fetch_user_v2(&id).await?;
Ok(UserResponse::V2(user))
},
"v3" => {
let user = fetch_user_v3(&id).await?;
Ok(UserResponse::V3(user))
},
_ => Err(async_graphql::Error::new("Unsupported version")),
}
}
async fn users(
&self,
ctx: &Context<'_>,
version: Option<String>,
#[graphql(desc = "Filter by user status")] status: Option<String>
) -> Result<Vec<UserResponse>> {
let version_registry = ctx.data::<VersionRegistry>()?;
let requested_version = version.unwrap_or_else(|| version_registry.get_default_version().to_string());
if !version_registry.is_supported(&requested_version) {
return Err(async_graphql::Error::new(format!("Version {} is not supported", requested_version)));
}
let users = match requested_version.as_str() {
"v1" => {
let v1_users = fetch_users_v1(status).await?;
v1_users.into_iter().map(UserResponse::V1).collect()
},
"v2" => {
let v2_users = fetch_users_v2(status).await?;
v2_users.into_iter().map(UserResponse::V2).collect()
},
"v3" => {
let v3_users = fetch_users_v3(status).await?;
v3_users.into_iter().map(UserResponse::V3).collect()
},
_ => return Err(async_graphql::Error::new("Unsupported version")),
};
Ok(users)
}
}
#[derive(SimpleObject, Clone)]
#[graphql(name = "UserV1")]
pub struct UserV1 {
pub id: String,
pub name: String,
pub email: String,
pub created_at: DateTime<Utc>,
}
#[derive(SimpleObject, Clone)]
#[graphql(name = "UserV2")]
pub struct UserV2 {
pub id: String,
pub name: String,
pub email: String,
pub profile: UserProfile,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub last_login: Option<DateTime<Utc>>,
pub permissions: Vec<String>,
}
#[derive(SimpleObject, Clone)]
#[graphql(name = "UserV3")]
pub struct UserV3 {
pub id: String,
pub name: String,
pub email: String,
pub profile: UserProfileV3,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub last_login: Option<DateTime<Utc>>,
pub permissions: Vec<String>,
pub security_context: SecurityContext,
pub compliance_flags: ComplianceFlags,
}
#[derive(SimpleObject, Clone)]
pub struct UserProfile {
pub bio: Option<String>,
pub avatar_url: Option<String>,
pub preferences: serde_json::Value,
}
#[derive(SimpleObject, Clone)]
pub struct UserProfileV3 {
pub bio: Option<String>,
pub avatar_url: Option<String>,
pub preferences: serde_json::Value,
pub privacy_settings: PrivacySettings,
pub notification_settings: NotificationSettings,
}
#[derive(SimpleObject, Clone)]
pub struct PrivacySettings {
pub profile_visibility: String,
pub data_sharing: bool,
pub analytics_opt_out: bool,
}
#[derive(SimpleObject, Clone)]
pub struct NotificationSettings {
pub email_notifications: bool,
pub push_notifications: bool,
pub security_alerts: bool,
}
#[derive(SimpleObject, Clone)]
pub struct SecurityContext {
pub mfa_enabled: bool,
pub last_password_change: DateTime<Utc>,
pub failed_login_attempts: u32,
pub device_trust_score: f64,
}
#[derive(SimpleObject, Clone)]
pub struct ComplianceFlags {
pub gdpr_compliant: bool,
pub data_retention_days: u32,
pub audit_required: bool,
pub classification: String,
}
#[derive(Union, Clone)]
pub enum UserResponse {
V1(UserV1),
V2(UserV2),
V3(UserV3),
}
pub struct DeprecationManager {
deprecations: HashMap<String, ApiDeprecation>,
}
#[derive(SimpleObject, Clone)]
pub struct ApiDeprecation {
pub deprecated_version: String,
pub removal_date: DateTime<Utc>,
pub alternative: String,
pub migration_guide: String,
pub warning_message: String,
pub urgency: DeprecationUrgency,
}
impl DeprecationManager {
pub fn new() -> Self {
let mut manager = Self {
deprecations: HashMap::new(),
};
manager.register_deprecation(ApiDeprecation {
deprecated_version: "v1".to_string(),
removal_date: DateTime::parse_from_rfc3339("2024-12-31T23:59:59Z").unwrap().with_timezone(&Utc),
alternative: "v2".to_string(),
migration_guide: "https://docs.fortress.security/migration/v1-to-v2".to_string(),
warning_message: "API v1 is deprecated and will be removed on 2024-12-31. Please migrate to v2.".to_string(),
urgency: DeprecationUrgency::High,
});
manager
}
pub fn register_deprecation(&mut self, deprecation: ApiDeprecation) {
self.deprecations.insert(deprecation.deprecated_version.clone(), deprecation);
}
pub fn check_deprecation(&self, requested_version: &str) -> Option<DeprecationWarning> {
if let Some(deprecation) = self.deprecations.get(requested_version) {
Some(DeprecationWarning {
message: deprecation.warning_message.clone(),
removal_date: deprecation.removal_date,
alternative: deprecation.alternative.clone(),
migration_guide: deprecation.migration_guide.clone(),
urgency: deprecation.urgency.clone(),
})
} else {
None
}
}
pub fn get_upcoming_deprecations(&self) -> Vec<&ApiDeprecation> {
let now = Utc::now();
self.deprecations
.values()
.filter(|d| d.removal_date > now)
.collect()
}
pub fn get_expired_deprecations(&self) -> Vec<&ApiDeprecation> {
let now = Utc::now();
self.deprecations
.values()
.filter(|d| d.removal_date <= now)
.collect()
}
}
pub struct VersionResolver;
#[Object]
impl VersionResolver {
async fn version_info(&self, ctx: &Context<'_>) -> Result<VersionInfo> {
let version_registry = ctx.data::<VersionRegistry>()?;
let supported = version_registry.list_supported_versions().into_iter().cloned().collect();
let deprecated = version_registry.list_deprecated_versions().into_iter().cloned().collect();
let removed = vec![];
let mut release_notes = HashMap::new();
release_notes.insert("v1".to_string(), ReleaseNotes {
version: "v1".to_string(),
release_date: DateTime::parse_from_rfc3339("2023-01-15T00:00:00Z").unwrap().with_timezone(&Utc),
features: vec![
"Basic authentication".to_string(),
"Simple encryption".to_string(),
"Basic queries".to_string(),
],
bug_fixes: vec![
"Fixed memory leak in encryption module".to_string(),
"Improved error handling".to_string(),
],
security_updates: vec![
"Updated cryptographic libraries".to_string(),
"Fixed authentication bypass".to_string(),
],
breaking_changes: vec![],
migration_notes: None,
});
release_notes.insert("v2".to_string(), ReleaseNotes {
version: "v2".to_string(),
release_date: DateTime::parse_from_rfc3339("2023-06-01T00:00:00Z").unwrap().with_timezone(&Utc),
features: vec![
"Enhanced authentication with MFA".to_string(),
"Field-level encryption".to_string(),
"Real-time subscriptions".to_string(),
"Performance monitoring".to_string(),
],
bug_fixes: vec![
"Fixed connection pooling issues".to_string(),
"Improved cache performance".to_string(),
],
security_updates: vec![
"Added rate limiting".to_string(),
"Enhanced input validation".to_string(),
],
breaking_changes: vec![
BreakingChange {
component: "authentication".to_string(),
description: "JWT token format changed".to_string(),
migration_required: true,
affected_operations: vec!["login".to_string()],
},
],
migration_notes: Some("Update authentication flow to handle new token format".to_string()),
});
release_notes.insert("v3".to_string(), ReleaseNotes {
version: "v3".to_string(),
release_date: DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z").unwrap().with_timezone(&Utc),
features: vec![
"Quantum-resistant cryptography".to_string(),
"Advanced compliance reporting".to_string(),
"ML-powered analytics".to_string(),
"Zero-knowledge proofs".to_string(),
],
bug_fixes: vec![
"Fixed performance regression in queries".to_string(),
"Improved memory efficiency".to_string(),
],
security_updates: vec![
"Added post-quantum encryption support".to_string(),
"Enhanced audit logging".to_string(),
],
breaking_changes: vec![
BreakingChange {
component: "encryption".to_string(),
description: "Default algorithm changed to AEGIS256".to_string(),
migration_required: true,
affected_operations: vec!["encrypt".to_string(), "decrypt".to_string()],
},
],
migration_notes: Some("Migrate existing encrypted data to new algorithm".to_string()),
});
Ok(VersionInfo {
current: version_registry.get_default_version().to_string(),
supported,
deprecated,
removed,
default_version: version_registry.get_default_version().to_string(),
release_notes,
})
}
async fn is_version_supported(
&self,
ctx: &Context<'_>,
version: String
) -> Result<bool> {
let version_registry = ctx.data::<VersionRegistry>()?;
Ok(version_registry.is_supported(&version))
}
async fn deprecation_warnings(
&self,
ctx: &Context<'_>,
version: String
) -> Result<Option<DeprecationWarning>> {
let version_registry = ctx.data::<VersionRegistry>()?;
let deprecation_manager = &version_registry.deprecation_manager;
Ok(deprecation_manager.check_deprecation(&version))
}
async fn upcoming_deprecations(&self, ctx: &Context<'_>) -> Result<Vec<ApiDeprecation>> {
let version_registry = ctx.data::<VersionRegistry>()?;
let deprecation_manager = &version_registry.deprecation_manager;
Ok(deprecation_manager.get_upcoming_deprecations().into_iter().cloned().collect())
}
}
pub struct MigrationResolver;
#[Object]
impl MigrationResolver {
async fn generate_migration_script(
&self,
ctx: &Context<'_>,
from_version: String,
to_version: String
) -> Result<MigrationScript> {
let version_registry = ctx.data::<VersionRegistry>()?;
if !version_registry.is_supported(&to_version) {
return Err(async_graphql::Error::new(format!("Target version {} is not supported", to_version)));
}
let script = match (from_version.as_str(), to_version.as_str()) {
("v1", "v2") => MigrationScript {
from_version: from_version.clone(),
to_version: to_version.clone(),
steps: vec![
MigrationStep {
description: "Update user queries to include profile information".to_string(),
code: r#"
# Old query
query GetUser($id: ID!) {
user(id: $id, version: "v1") {
id
name
email
createdAt
}
}
# New query
query GetUser($id: ID!) {
user(id: $id, version: "v2") {
id
name
email
profile {
bio
avatarUrl
}
createdAt
updatedAt
lastLogin
permissions
}
}"#.to_string(),
breaking_change: false,
estimated_duration_secs: 30,
verification_query: Some("query { user(id: \"test\", version: \"v2\") { id } }".to_string()),
},
MigrationStep {
description: "Update authentication to use enhanced JWT tokens".to_string(),
code: r#"
# Old authentication
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
user {
id
name
}
}
}
# New authentication
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
refreshToken
expiresAt
user {
id
name
permissions
}
}
}"#.to_string(),
breaking_change: true,
estimated_duration_secs: 60,
verification_query: Some("mutation { login(email: \"test\", password: \"test\") { token } }".to_string()),
},
],
estimated_time_secs: 7200,
complexity: MigrationComplexity::Medium,
prerequisites: vec![
"Backup current authentication tokens".to_string(),
"Update client libraries to v2".to_string(),
],
rollback_available: true,
},
("v2", "v3") => MigrationScript {
from_version: from_version.clone(),
to_version: to_version.clone(),
steps: vec![
MigrationStep {
description: "Update encryption to use quantum-resistant algorithms".to_string(),
code: r#"
# Old encryption
mutation EncryptData($data: String!, $keyId: ID!) {
encrypt(data: $data, keyId: $keyId) {
encryptedData
algorithm
}
}
# New encryption
mutation EncryptData($data: String!, $keyId: ID!) {
encrypt(data: $data, keyId: $keyId) {
encryptedData
algorithm
quantumResistant
metadata {
encryptionVersion
keyDerivation
}
}
}
}"#.to_string(),
breaking_change: true,
estimated_duration_secs: 45,
verification_query: Some("mutation { encrypt(data: \"test\", keyId: \"test\") { algorithm } }".to_string()),
},
MigrationStep {
description: "Enable advanced compliance features".to_string(),
code: r#"
# Update compliance configuration
mutation UpdateComplianceConfig {
updateComplianceConfig(config: {
framework: "gdpr"
advancedFeatures: true
quantumResistant: true
}) {
success
message
}
}"#.to_string(),
breaking_change: false,
estimated_duration_secs: 15,
verification_query: Some("query { complianceConfig { framework } }".to_string()),
},
],
estimated_time_secs: 14400,
complexity: MigrationComplexity::High,
prerequisites: vec![
"Generate new quantum-resistant keys".to_string(),
"Update compliance documentation".to_string(),
"Test migration in staging environment".to_string(),
],
rollback_available: true,
},
_ => return Err(async_graphql::Error::new("Migration path not supported")),
};
Ok(script)
}
async fn validate_migration_readiness(
&self,
ctx: &Context<'_>,
from_version: String,
to_version: String
) -> Result<MigrationReadiness> {
let version_registry = ctx.data::<VersionRegistry>()?;
let migration_path = version_registry.get_migration_path(&from_version, &to_version)
.ok_or_else(|| async_graphql::Error::new("No migration path available"))?;
let checks = vec![
ReadinessCheck {
name: "Version compatibility".to_string(),
status: CheckStatus::Pass,
message: "Source and target versions are compatible".to_string(),
},
ReadinessCheck {
name: "Backup availability".to_string(),
status: CheckStatus::Warning,
message: "Ensure recent backup is available before migration".to_string(),
},
ReadinessCheck {
name: "Client compatibility".to_string(),
status: CheckStatus::Pass,
message: "Client libraries support target version".to_string(),
},
ReadinessCheck {
name: "Resource availability".to_string(),
status: CheckStatus::Pass,
message: "Sufficient resources available for migration".to_string(),
},
];
let estimated_downtime = std::time::Duration::from_secs(900);
Ok(MigrationReadiness {
ready: checks.iter().all(|c| matches!(c.status, CheckStatus::Pass)),
migration_path,
checks,
estimated_downtime_secs: estimated_downtime.as_secs(),
recommended_maintenance_window: Some("Weekend 2-4 AM".to_string()),
})
}
}
#[derive(SimpleObject)]
pub struct MigrationReadiness {
pub ready: bool,
pub migration_path: Vec<String>,
pub checks: Vec<ReadinessCheck>,
pub estimated_downtime_secs: u64,
pub recommended_maintenance_window: Option<String>,
}
#[derive(SimpleObject, Clone)]
pub struct ReadinessCheck {
pub name: String,
pub status: CheckStatus,
pub message: String,
}
async fn fetch_user_v1(_id: &str) -> Result<UserV1> {
Ok(UserV1 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
created_at: Utc::now(),
})
}
async fn fetch_user_v2(_id: &str) -> Result<UserV2> {
Ok(UserV2 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
profile: UserProfile {
bio: Some("Software Developer".to_string()),
avatar_url: Some("https://example.com/avatar.jpg".to_string()),
preferences: serde_json::json!({"theme": "dark"}),
},
created_at: Utc::now(),
updated_at: Utc::now(),
last_login: Some(Utc::now()),
permissions: vec!["read".to_string(), "write".to_string()],
})
}
async fn fetch_user_v3(_id: &str) -> Result<UserV3> {
Ok(UserV3 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
profile: UserProfileV3 {
bio: Some("Software Developer".to_string()),
avatar_url: Some("https://example.com/avatar.jpg".to_string()),
preferences: serde_json::json!({"theme": "dark"}),
privacy_settings: PrivacySettings {
profile_visibility: "public".to_string(),
data_sharing: false,
analytics_opt_out: true,
},
notification_settings: NotificationSettings {
email_notifications: true,
push_notifications: false,
security_alerts: true,
},
},
created_at: Utc::now(),
updated_at: Utc::now(),
last_login: Some(Utc::now()),
permissions: vec!["read".to_string(), "write".to_string(), "admin".to_string()],
security_context: SecurityContext {
mfa_enabled: true,
last_password_change: Utc::now(),
failed_login_attempts: 0,
device_trust_score: 0.95,
},
compliance_flags: ComplianceFlags {
gdpr_compliant: true,
data_retention_days: 365,
audit_required: true,
classification: "confidential".to_string(),
},
})
}
async fn fetch_users_v1(_status: Option<String>) -> Result<Vec<UserV1>> {
Ok(vec![
UserV1 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
created_at: Utc::now(),
}
])
}
async fn fetch_users_v2(_status: Option<String>) -> Result<Vec<UserV2>> {
Ok(vec![
UserV2 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
profile: UserProfile {
bio: Some("Software Developer".to_string()),
avatar_url: Some("https://example.com/avatar.jpg".to_string()),
preferences: serde_json::json!({"theme": "dark"}),
},
created_at: Utc::now(),
updated_at: Utc::now(),
last_login: Some(Utc::now()),
permissions: vec!["read".to_string(), "write".to_string()],
}
])
}
async fn fetch_users_v3(_status: Option<String>) -> Result<Vec<UserV3>> {
Ok(vec![
UserV3 {
id: "user_123".to_string(),
name: "John Doe".to_string(),
email: "john@example.com".to_string(),
profile: UserProfileV3 {
bio: Some("Software Developer".to_string()),
avatar_url: Some("https://example.com/avatar.jpg".to_string()),
preferences: serde_json::json!({"theme": "dark"}),
privacy_settings: PrivacySettings {
profile_visibility: "public".to_string(),
data_sharing: false,
analytics_opt_out: true,
},
notification_settings: NotificationSettings {
email_notifications: true,
push_notifications: false,
security_alerts: true,
},
},
created_at: Utc::now(),
updated_at: Utc::now(),
last_login: Some(Utc::now()),
permissions: vec!["read".to_string(), "write".to_string(), "admin".to_string()],
security_context: SecurityContext {
mfa_enabled: true,
last_password_change: Utc::now(),
failed_login_attempts: 0,
device_trust_score: 0.95,
},
compliance_flags: ComplianceFlags {
gdpr_compliant: true,
data_retention_days: 365,
audit_required: true,
classification: "confidential".to_string(),
},
}
])
}