use serde_json::{Value, json};
use crate::config::BucketConfig;
pub struct StorageSchemaTypes;
#[derive(Debug, Default)]
pub struct StorageSchemaEntries {
pub types: Vec<Value>,
pub mutations: Vec<Value>,
pub queries: Vec<Value>,
}
impl StorageSchemaTypes {
#[must_use]
pub fn generate(buckets: &[BucketConfig]) -> StorageSchemaEntries {
let mut entries = StorageSchemaEntries::default();
for bucket in buckets {
entries.types.push(Self::storage_object_type(bucket));
entries.mutations.push(Self::upload_url_mutation(bucket));
entries.queries.push(Self::list_query(bucket));
}
entries
}
#[must_use]
pub fn storage_object_type(bucket: &BucketConfig) -> Value {
let type_name = format!("{}StorageObject", Self::bucket_type_name(&bucket.name));
json!({
"name": type_name,
"sql_source": format!("t_storage_{}", bucket.name),
"jsonb_column": "data",
"description": format!("Storage object in the {} bucket", bucket.name),
"fields": [
{
"name": "key",
"field_type": "String",
"description": "Object key in the bucket"
},
{
"name": "size",
"field_type": "Int",
"description": "Size in bytes"
},
{
"name": "content_type",
"field_type": "String",
"description": "MIME type"
},
{
"name": "created_at",
"field_type": "DateTime",
"description": "Upload timestamp"
},
{
"name": "updated_at",
"field_type": "DateTime",
"nullable": true,
"description": "Last modified timestamp"
}
]
})
}
#[must_use]
pub fn upload_url_mutation(bucket: &BucketConfig) -> Value {
let mutation_name = format!("generate{}UploadUrl", Self::bucket_type_name(&bucket.name));
json!({
"name": mutation_name,
"description": format!("Generate presigned upload URL for {} bucket", bucket.name),
"operation": "Custom",
"return_type": "PresignedUrlResponse",
"arguments": [
{
"name": "key",
"arg_type": "String",
"description": "Object key"
},
{
"name": "content_type",
"arg_type": "String",
"description": "MIME type of the file being uploaded"
}
]
})
}
#[must_use]
pub fn list_query(bucket: &BucketConfig) -> Value {
let query_name = format!("list{}Objects", Self::bucket_type_name(&bucket.name));
let return_type_name = format!("{}StorageObject", Self::bucket_type_name(&bucket.name));
json!({
"name": query_name,
"description": format!("List objects in {} bucket", bucket.name),
"return_type": return_type_name,
"returns_list": true,
"sql_source": format!("t_storage_{}", bucket.name),
"jsonb_column": "data",
"arguments": [
{
"name": "prefix",
"arg_type": "String",
"nullable": true,
"description": "Filter by key prefix"
},
{
"name": "limit",
"arg_type": "Int",
"nullable": true,
"description": "Maximum number of results"
},
{
"name": "cursor",
"arg_type": "String",
"nullable": true,
"description": "Pagination cursor"
}
]
})
}
#[must_use]
pub fn bucket_type_name(bucket_name: &str) -> String {
bucket_name
.split(['_', '-'])
.filter(|s| !s.is_empty())
.map(|s| {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect()
}
}
#[cfg(test)]
mod tests;