core: type auth profile timestamps

This commit is contained in:
a.tolmachev
2026-04-12 22:20:19 +00:00
parent e03d4da925
commit edf318ba67
10 changed files with 90 additions and 42 deletions
@@ -1,6 +1,6 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "select\n id,\n workspace_id,\n name,\n kind,\n config_json,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",\n to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\"\n from auth_profiles\n where workspace_id = $1\n order by name asc", "query": "select\n id,\n workspace_id,\n name,\n kind,\n config_json,\n created_at as \"created_at!: OffsetDateTime\",\n updated_at as \"updated_at!: OffsetDateTime\"\n from auth_profiles\n where workspace_id = $1\n order by name asc",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -30,13 +30,13 @@
}, },
{ {
"ordinal": 5, "ordinal": 5,
"name": "created_at!", "name": "created_at!: OffsetDateTime",
"type_info": "Text" "type_info": "Timestamptz"
}, },
{ {
"ordinal": 6, "ordinal": 6,
"name": "updated_at!", "name": "updated_at!: OffsetDateTime",
"type_info": "Text" "type_info": "Timestamptz"
} }
], ],
"parameters": { "parameters": {
@@ -50,9 +50,9 @@
false, false,
false, false,
false, false,
null, false,
null false
] ]
}, },
"hash": "10d71ae9e9d2c39546ab0075809d0c17742dd5b6377f40704dfec1c76f51f839" "hash": "b86a7406eeec83390458e35fb8611e19377582ec0ca245a162c2d2236077bb9b"
} }
@@ -1,6 +1,6 @@
{ {
"db_name": "PostgreSQL", "db_name": "PostgreSQL",
"query": "select\n id,\n workspace_id,\n name,\n kind,\n config_json,\n to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\",\n to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\"\n from auth_profiles\n where workspace_id = $1 and id = $2", "query": "select\n id,\n workspace_id,\n name,\n kind,\n config_json,\n created_at as \"created_at!: OffsetDateTime\",\n updated_at as \"updated_at!: OffsetDateTime\"\n from auth_profiles\n where workspace_id = $1 and id = $2",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -30,13 +30,13 @@
}, },
{ {
"ordinal": 5, "ordinal": 5,
"name": "created_at!", "name": "created_at!: OffsetDateTime",
"type_info": "Text" "type_info": "Timestamptz"
}, },
{ {
"ordinal": 6, "ordinal": 6,
"name": "updated_at!", "name": "updated_at!: OffsetDateTime",
"type_info": "Text" "type_info": "Timestamptz"
} }
], ],
"parameters": { "parameters": {
@@ -51,9 +51,9 @@
false, false,
false, false,
false, false,
null, false,
null false
] ]
}, },
"hash": "f51d113d5f026b0b11e086cc5a37578dcc6977ce6eb90a8fc3afe63023598c06" "hash": "fadf243775a355c46ae97d26049793eb1d1ad17505b1e1609a842750e3233ec2"
} }
Generated
+1
View File
@@ -501,6 +501,7 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-tungstenite", "tokio-tungstenite",
] ]
+2 -2
View File
@@ -3225,14 +3225,14 @@ impl AdminService {
self.validate_auth_profile_secret_ids(workspace_id, &payload.config) self.validate_auth_profile_secret_ids(workspace_id, &payload.config)
.await?; .await?;
let now = now_string()?; let now = OffsetDateTime::now_utc();
let profile = AuthProfile { let profile = AuthProfile {
id: AuthProfileId::new(new_prefixed_id("auth")), id: AuthProfileId::new(new_prefixed_id("auth")),
workspace_id: workspace_id.clone(), workspace_id: workspace_id.clone(),
name: payload.name, name: payload.name,
kind: payload.kind, kind: payload.kind,
config: payload.config, config: payload.config,
created_at: now.clone(), created_at: now,
updated_at: now, updated_at: now,
}; };
+38 -2
View File
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::{ use crate::{
ids::{AuthProfileId, SecretId, WorkspaceId}, ids::{AuthProfileId, SecretId, WorkspaceId},
@@ -56,6 +57,41 @@ pub struct AuthProfile {
pub name: String, pub name: String,
pub kind: AuthKind, pub kind: AuthKind,
pub config: AuthConfig, pub config: AuthConfig,
pub created_at: String, #[serde(with = "time::serde::rfc3339")]
pub updated_at: String, pub created_at: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub updated_at: OffsetDateTime,
}
#[cfg(test)]
mod tests {
use serde_json::json;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use super::{ApiKeyHeaderAuthConfig, AuthConfig, AuthProfile};
use crate::{
ids::{AuthProfileId, SecretId, WorkspaceId},
protocol::AuthKind,
};
#[test]
fn auth_profile_serializes_timestamps_as_rfc3339() {
let profile = AuthProfile {
id: AuthProfileId::new("auth_01"),
workspace_id: WorkspaceId::new("ws_default"),
name: "header".to_owned(),
kind: AuthKind::ApiKeyHeader,
config: AuthConfig::ApiKeyHeader(ApiKeyHeaderAuthConfig {
header_name: "X-Api-Key".to_owned(),
secret_id: SecretId::new("secret_01"),
}),
created_at: OffsetDateTime::parse("2026-03-25T12:00:00Z", &Rfc3339).unwrap(),
updated_at: OffsetDateTime::parse("2026-03-25T12:05:00Z", &Rfc3339).unwrap(),
};
let value = serde_json::to_value(&profile).unwrap();
assert_eq!(value["created_at"], json!("2026-03-25T12:00:00Z"));
assert_eq!(value["updated_at"], json!("2026-03-25T12:05:00Z"));
}
} }
+7 -2
View File
@@ -264,6 +264,7 @@ mod tests {
use std::collections::BTreeMap; use std::collections::BTreeMap;
use serde_json::json; use serde_json::json;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crate::{ use crate::{
auth::{AuthConfig, AuthProfile, BearerAuthConfig}, auth::{AuthConfig, AuthProfile, BearerAuthConfig},
@@ -280,6 +281,10 @@ mod tests {
}, },
}; };
fn timestamp(value: &str) -> OffsetDateTime {
OffsetDateTime::parse(value, &Rfc3339).unwrap()
}
#[test] #[test]
fn rest_target_serializes_with_kind_tag() { fn rest_target_serializes_with_kind_tag() {
let target = Target::Rest(RestTarget { let target = Target::Rest(RestTarget {
@@ -440,8 +445,8 @@ mod tests {
header_name: "Authorization".to_owned(), header_name: "Authorization".to_owned(),
secret_id: crate::ids::SecretId::new("secret_crm_prod_token"), secret_id: crate::ids::SecretId::new("secret_crm_prod_token"),
}), }),
created_at: "2026-03-25T08:00:00Z".to_owned(), created_at: timestamp("2026-03-25T08:00:00Z"),
updated_at: "2026-03-25T08:00:00Z".to_owned(), updated_at: timestamp("2026-03-25T08:00:00Z"),
}; };
let value = serde_json::to_value(profile).unwrap(); let value = serde_json::to_value(profile).unwrap();
+6 -6
View File
@@ -389,8 +389,8 @@ impl PostgresRegistry {
.bind(&request.profile.name) .bind(&request.profile.name)
.bind(serialize_enum_text(&request.profile.kind, "kind")?) .bind(serialize_enum_text(&request.profile.kind, "kind")?)
.bind(Json(serialize_json_value(&request.profile.config)?)) .bind(Json(serialize_json_value(&request.profile.config)?))
.bind(&request.profile.created_at) .bind(request.profile.created_at)
.bind(&request.profile.updated_at) .bind(request.profile.updated_at)
.execute(&self.pool) .execute(&self.pool)
.await?; .await?;
@@ -409,8 +409,8 @@ impl PostgresRegistry {
name, name,
kind, kind,
config_json, config_json,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\", created_at as \"created_at!: OffsetDateTime\",
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\" updated_at as \"updated_at!: OffsetDateTime\"
from auth_profiles from auth_profiles
where workspace_id = $1 and id = $2", where workspace_id = $1 and id = $2",
workspace_id.as_str(), workspace_id.as_str(),
@@ -444,8 +444,8 @@ impl PostgresRegistry {
name, name,
kind, kind,
config_json, config_json,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"created_at!\", created_at as \"created_at!: OffsetDateTime\",
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as \"updated_at!\" updated_at as \"updated_at!: OffsetDateTime\"
from auth_profiles from auth_profiles
where workspace_id = $1 where workspace_id = $1
order by name asc", order by name asc",
+6 -6
View File
@@ -750,8 +750,8 @@ fn build_auth_profile(
name: String, name: String,
kind: String, kind: String,
config_json: Value, config_json: Value,
created_at: String, created_at: OffsetDateTime,
updated_at: String, updated_at: OffsetDateTime,
) -> Result<AuthProfile, RegistryError> { ) -> Result<AuthProfile, RegistryError> {
Ok(AuthProfile { Ok(AuthProfile {
id: crank_core::AuthProfileId::new(id), id: crank_core::AuthProfileId::new(id),
@@ -1315,8 +1315,8 @@ mod tests {
header_name: "X-Api-Key".to_owned(), header_name: "X-Api-Key".to_owned(),
secret_id: SecretId::new("secret_crank_api_key"), secret_id: SecretId::new("secret_crank_api_key"),
}), }),
created_at: "2026-03-25T12:00:00Z".to_owned(), created_at: timestamp("2026-03-25T12:00:00Z"),
updated_at: "2026-03-25T12:00:00Z".to_owned(), updated_at: timestamp("2026-03-25T12:00:00Z"),
}; };
registry registry
@@ -1396,8 +1396,8 @@ mod tests {
username_secret_id: primary_secret_id.clone(), username_secret_id: primary_secret_id.clone(),
password_secret_id: secondary_secret_id.clone(), password_secret_id: secondary_secret_id.clone(),
}), }),
created_at: "2026-03-25T12:00:00Z".to_owned(), created_at: timestamp("2026-03-25T12:00:00Z"),
updated_at: "2026-03-25T12:00:00Z".to_owned(), updated_at: timestamp("2026-03-25T12:00:00Z"),
}; };
registry registry
+1
View File
@@ -26,5 +26,6 @@ thiserror.workspace = true
axum.workspace = true axum.workspace = true
crank-adapter-grpc = { path = "../crank-adapter-grpc", features = ["test-support"] } crank-adapter-grpc = { path = "../crank-adapter-grpc", features = ["test-support"] }
futures-util = "0.3" futures-util = "0.3"
time.workspace = true
tokio.workspace = true tokio.workspace = true
tokio-tungstenite.workspace = true tokio-tungstenite.workspace = true
+13 -8
View File
@@ -120,9 +120,14 @@ mod tests {
BasicAuthConfig, BearerAuthConfig, SecretId, WorkspaceId, BasicAuthConfig, BearerAuthConfig, SecretId, WorkspaceId,
}; };
use serde_json::json; use serde_json::json;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use crate::{PreparedRequest, auth::ResolvedAuth}; use crate::{PreparedRequest, auth::ResolvedAuth};
fn timestamp(value: &str) -> OffsetDateTime {
OffsetDateTime::parse(value, &Rfc3339).unwrap()
}
#[test] #[test]
fn applies_bearer_auth_to_headers() { fn applies_bearer_auth_to_headers() {
let auth = ResolvedAuth::from_profile( let auth = ResolvedAuth::from_profile(
@@ -135,8 +140,8 @@ mod tests {
header_name: "Authorization".to_owned(), header_name: "Authorization".to_owned(),
secret_id: SecretId::new("secret_token"), secret_id: SecretId::new("secret_token"),
}), }),
created_at: "2026-04-07T00:00:00Z".to_owned(), created_at: timestamp("2026-04-07T00:00:00Z"),
updated_at: "2026-04-07T00:00:00Z".to_owned(), updated_at: timestamp("2026-04-07T00:00:00Z"),
}, },
&BTreeMap::from([(SecretId::new("secret_token"), json!({"token":"abc"}))]), &BTreeMap::from([(SecretId::new("secret_token"), json!({"token":"abc"}))]),
) )
@@ -163,8 +168,8 @@ mod tests {
username_secret_id: SecretId::new("secret_user"), username_secret_id: SecretId::new("secret_user"),
password_secret_id: SecretId::new("secret_pass"), password_secret_id: SecretId::new("secret_pass"),
}), }),
created_at: "2026-04-07T00:00:00Z".to_owned(), created_at: timestamp("2026-04-07T00:00:00Z"),
updated_at: "2026-04-07T00:00:00Z".to_owned(), updated_at: timestamp("2026-04-07T00:00:00Z"),
}, },
&BTreeMap::from([ &BTreeMap::from([
(SecretId::new("secret_user"), json!({"username":"demo"})), (SecretId::new("secret_user"), json!({"username":"demo"})),
@@ -194,8 +199,8 @@ mod tests {
header_name: "X-Api-Key".to_owned(), header_name: "X-Api-Key".to_owned(),
secret_id: SecretId::new("secret_api_key"), secret_id: SecretId::new("secret_api_key"),
}), }),
created_at: "2026-04-07T00:00:00Z".to_owned(), created_at: timestamp("2026-04-07T00:00:00Z"),
updated_at: "2026-04-07T00:00:00Z".to_owned(), updated_at: timestamp("2026-04-07T00:00:00Z"),
}, },
&BTreeMap::from([(SecretId::new("secret_api_key"), json!("key-123"))]), &BTreeMap::from([(SecretId::new("secret_api_key"), json!("key-123"))]),
) )
@@ -222,8 +227,8 @@ mod tests {
param_name: "api_key".to_owned(), param_name: "api_key".to_owned(),
secret_id: SecretId::new("secret_api_key"), secret_id: SecretId::new("secret_api_key"),
}), }),
created_at: "2026-04-07T00:00:00Z".to_owned(), created_at: timestamp("2026-04-07T00:00:00Z"),
updated_at: "2026-04-07T00:00:00Z".to_owned(), updated_at: timestamp("2026-04-07T00:00:00Z"),
}, },
&BTreeMap::from([(SecretId::new("secret_api_key"), json!({"value":"key-123"}))]), &BTreeMap::from([(SecretId::new("secret_api_key"), json!({"value":"key-123"}))]),
) )