use std::marker::PhantomData;
use std::sync::Arc;
use bytes::Bytes;
use uuid::Uuid;
use crate::label::Label;
use crate::name::ResourceName;
use crate::object::Object;
use crate::registry::{FieldRole, ResourceRegistry};
use crate::secrets::SecretManager;
use crate::store::{ObjectStore, ObjectStoreReader};
use crate::{Error, Result};
pub struct ManagedObjectStore<L: Label, S, M = NoSecrets> {
inner: S,
secrets: M,
registry: Arc<ResourceRegistry<L>>,
_label: PhantomData<L>,
}
pub struct NoSecrets;
impl<L: Label, S: ObjectStore<L>> ManagedObjectStore<L, S, NoSecrets> {
pub fn new(inner: S, registry: ResourceRegistry<L>) -> Self {
Self {
inner,
secrets: NoSecrets,
registry: Arc::new(registry),
_label: PhantomData,
}
}
}
impl<L: Label, S: ObjectStore<L>, M: SecretManager> ManagedObjectStore<L, S, M> {
pub fn with_secrets(inner: S, secrets: M, registry: ResourceRegistry<L>) -> Self {
Self {
inner,
secrets,
registry: Arc::new(registry),
_label: PhantomData,
}
}
}
impl<L: Label, S, M> ManagedObjectStore<L, S, M> {
fn strip_fields(
&self,
label: L,
properties: Option<serde_json::Value>,
) -> (
Option<serde_json::Value>,
Option<serde_json::Map<String, serde_json::Value>>,
) {
let Some(serde_json::Value::Object(mut map)) = properties else {
return (properties, None);
};
let Some(descriptor) = self.registry.get(label) else {
return (Some(serde_json::Value::Object(map)), None);
};
let mut sensitive_map = serde_json::Map::new();
for field in descriptor.fields.iter() {
match field.role {
FieldRole::Identifier | FieldRole::Managed => {
map.remove(field.name);
}
FieldRole::Sensitive => {
if let Some(value) = map.remove(field.name) {
sensitive_map.insert(field.name.to_string(), value);
}
}
FieldRole::Data => {
}
}
}
let sensitive = if sensitive_map.is_empty() {
None
} else {
Some(sensitive_map)
};
(Some(serde_json::Value::Object(map)), sensitive)
}
fn inject_fields(&self, object: &mut Object<L>) {
let Some(descriptor) = self.registry.get(object.label) else {
return;
};
let map = object
.properties
.get_or_insert_with(|| serde_json::Value::Object(serde_json::Map::new()));
let Some(map) = map.as_object_mut() else {
return;
};
for field in descriptor.fields.iter() {
match field.role {
FieldRole::Identifier => {
map.insert(
field.name.to_string(),
serde_json::Value::String(object.id.to_string()),
);
}
FieldRole::Managed => {
match field.name {
"created_at" => {
map.insert(
field.name.to_string(),
serde_json::Value::String(object.created_at.to_rfc3339()),
);
}
"updated_at" => {
if let Some(updated) = object.updated_at {
map.insert(
field.name.to_string(),
serde_json::Value::String(updated.to_rfc3339()),
);
}
}
_ => {
}
}
}
FieldRole::Sensitive => {
map.remove(field.name);
}
FieldRole::Data => {
}
}
}
}
}
#[async_trait::async_trait]
impl<L: Label, S: ObjectStoreReader<L>, M: Send + Sync + 'static> ObjectStoreReader<L>
for ManagedObjectStore<L, S, M>
{
async fn get(&self, id: &Uuid) -> Result<Object<L>> {
let mut object = self.inner.get(id).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn get_by_name(&self, label: L, name: &ResourceName) -> Result<Object<L>> {
let mut object = self.inner.get_by_name(label, name).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn list(
&self,
label: L,
namespace: Option<&ResourceName>,
max_results: Option<usize>,
page_token: Option<String>,
) -> Result<(Vec<Object<L>>, Option<String>)> {
let (mut objects, token) = self
.inner
.list(label, namespace, max_results, page_token)
.await?;
for object in &mut objects {
self.inject_fields(object);
}
Ok((objects, token))
}
}
#[async_trait::async_trait]
impl<L: Label, S: ObjectStore<L>> ObjectStore<L> for ManagedObjectStore<L, S, NoSecrets> {
async fn create(
&self,
label: L,
name: &ResourceName,
properties: Option<serde_json::Value>,
) -> Result<Object<L>> {
let (stripped, _sensitive) = self.strip_fields(label, properties);
let mut object = self.inner.create(label, name, stripped).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn update(&self, id: &Uuid, properties: Option<serde_json::Value>) -> Result<Object<L>> {
let existing = self.inner.get(id).await?;
let (stripped, _sensitive) = self.strip_fields(existing.label, properties);
let mut object = self.inner.update(id, stripped).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn delete(&self, id: &Uuid) -> Result<()> {
self.inner.delete(id).await
}
}
#[async_trait::async_trait]
impl<L: Label, S: ObjectStore<L>, M: SecretManager> ObjectStore<L> for ManagedObjectStore<L, S, M> {
async fn create(
&self,
label: L,
name: &ResourceName,
properties: Option<serde_json::Value>,
) -> Result<Object<L>> {
let (stripped, sensitive) = self.strip_fields(label, properties);
if let Some(sensitive_map) = sensitive {
let secret_bytes = Bytes::from(serde_json::to_vec(&serde_json::Value::Object(
sensitive_map,
))?);
self.secrets
.create_secret(&name.to_string(), secret_bytes)
.await?;
}
let mut object = self.inner.create(label, name, stripped).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn update(&self, id: &Uuid, properties: Option<serde_json::Value>) -> Result<Object<L>> {
let existing = self.inner.get(id).await?;
let (stripped, sensitive) = self.strip_fields(existing.label, properties);
if let Some(sensitive_map) = sensitive {
let secret_bytes = Bytes::from(serde_json::to_vec(&serde_json::Value::Object(
sensitive_map,
))?);
let secret_name = existing.name.to_string();
match self
.secrets
.update_secret(&secret_name, secret_bytes.clone())
.await
{
Ok(_) => {}
Err(Error::NotFound) => {
self.secrets
.create_secret(&secret_name, secret_bytes)
.await?;
}
Err(e) => return Err(e),
}
}
let mut object = self.inner.update(id, stripped).await?;
self.inject_fields(&mut object);
Ok(object)
}
async fn delete(&self, id: &Uuid) -> Result<()> {
let object = self.inner.get(id).await?;
if self.registry.has_sensitive_fields(object.label) {
let secret_name = object.name.to_string();
match self.secrets.delete_secret(&secret_name).await {
Ok(()) | Err(Error::NotFound) => {}
Err(e) => return Err(e),
}
}
self.inner.delete(id).await
}
}
impl<L: Label, S: ObjectStore<L>, M: SecretManager> ManagedObjectStore<L, S, M> {
pub async fn get_with_secrets(&self, id: &Uuid) -> Result<Object<L>> {
let mut object = self.inner.get(id).await?;
self.inject_fields(&mut object);
if self.registry.has_sensitive_fields(object.label) {
let secret_name = object.name.to_string();
match self.secrets.get_secret(&secret_name).await {
Ok((_version, secret_bytes)) => {
let sensitive: serde_json::Value = serde_json::from_slice(&secret_bytes)?;
if let (Some(props), serde_json::Value::Object(secret_map)) =
(object.properties.as_mut(), sensitive)
{
if let Some(props_map) = props.as_object_mut() {
for (key, value) in secret_map {
props_map.insert(key, value);
}
}
}
}
Err(Error::NotFound) => {
}
Err(e) => return Err(e),
}
}
Ok(object)
}
}