use parse_rust_core::{new_object_id, ErrorCode, ParseDate, ParseError, ParseMap, ParseValue};
use parse_rust_schema::{apply, default_schema, validate_write};
use parse_rust_storage::{ClassSchema, Constraint, QueryOptions, StorageAdapter};
use crate::acl::{lower_acl, raise_acl, AclScope};
#[derive(Debug, Clone)]
pub struct CreateResponse {
pub object_id: String,
pub created_at: ParseDate,
}
#[derive(Debug, Clone)]
pub struct UpdateResponse {
pub updated_at: ParseDate,
}
async fn load_schema<S: StorageAdapter>(
storage: &S,
class_name: &str,
) -> Result<ClassSchema, ParseError> {
let all = storage.all_schemas().await?;
Ok(all
.into_iter()
.find(|s| s.class_name == class_name)
.unwrap_or_else(|| default_schema(class_name)))
}
pub async fn create<S: StorageAdapter>(
storage: &S,
class_name: &str,
body: ParseMap,
scope: &AclScope,
) -> Result<CreateResponse, ParseError> {
let mut schema = load_schema(storage, class_name).await?;
let object_id = match body.get("objectId") {
Some(ParseValue::String(id)) => id.clone(),
_ => new_object_id(),
};
let now = ParseDate::now();
let mut row = body;
row.insert(
"objectId".to_string(),
ParseValue::String(object_id.clone()),
);
row.insert("createdAt".to_string(), ParseValue::Date(now));
row.insert("updatedAt".to_string(), ParseValue::Date(now));
let delta = validate_write(&schema, &row)?;
let stored = lower_acl(row);
apply(&mut schema, &delta);
storage.create(&schema, &stored).await?;
if !delta.is_empty() {
storage.upsert_schema(&schema).await?;
}
let _ = scope; Ok(CreateResponse {
object_id,
created_at: now,
})
}
pub async fn find<S: StorageAdapter>(
storage: &S,
class_name: &str,
mut constraints: Vec<Constraint>,
options: QueryOptions,
scope: &AclScope,
) -> Result<Vec<ParseMap>, ParseError> {
let schema = load_schema(storage, class_name).await?;
if let Some(acl) = scope.read_constraint() {
constraints.push(acl);
}
let rows = storage.find(&schema, &constraints, &options).await?;
Ok(rows.into_iter().map(raise_acl).collect())
}
pub async fn get<S: StorageAdapter>(
storage: &S,
class_name: &str,
object_id: &str,
scope: &AclScope,
) -> Result<ParseMap, ParseError> {
let rows = find(
storage,
class_name,
vec![Constraint::equal(
"objectId",
ParseValue::String(object_id.to_string()),
)],
QueryOptions {
limit: Some(1),
..Default::default()
},
scope,
)
.await?;
rows.into_iter().next().ok_or_else(object_not_found)
}
pub async fn count<S: StorageAdapter>(
storage: &S,
class_name: &str,
mut constraints: Vec<Constraint>,
scope: &AclScope,
) -> Result<u64, ParseError> {
let schema = load_schema(storage, class_name).await?;
if let Some(acl) = scope.read_constraint() {
constraints.push(acl);
}
storage.count(&schema, &constraints).await
}
pub async fn update<S: StorageAdapter>(
storage: &S,
class_name: &str,
object_id: &str,
body: ParseMap,
scope: &AclScope,
) -> Result<UpdateResponse, ParseError> {
let mut schema = load_schema(storage, class_name).await?;
let now = ParseDate::now();
let mut row = body;
row.shift_remove("objectId");
row.shift_remove("createdAt");
row.insert("updatedAt".to_string(), ParseValue::Date(now));
let delta = validate_write(&schema, &row)?;
let values = lower_acl(row);
let mut constraints = vec![Constraint::equal(
"objectId",
ParseValue::String(object_id.to_string()),
)];
if let Some(acl) = scope.write_constraint() {
constraints.push(acl);
}
apply(&mut schema, &delta);
let matched = storage.update(&schema, &constraints, &values).await?;
if matched == 0 {
return Err(object_not_found());
}
if !delta.is_empty() {
storage.upsert_schema(&schema).await?;
}
Ok(UpdateResponse { updated_at: now })
}
pub async fn delete<S: StorageAdapter>(
storage: &S,
class_name: &str,
object_id: &str,
scope: &AclScope,
) -> Result<(), ParseError> {
let schema = load_schema(storage, class_name).await?;
let mut constraints = vec![Constraint::equal(
"objectId",
ParseValue::String(object_id.to_string()),
)];
if let Some(acl) = scope.write_constraint() {
constraints.push(acl);
}
let deleted = storage.delete(&schema, &constraints).await?;
if deleted == 0 {
return Err(object_not_found());
}
Ok(())
}
fn object_not_found() -> ParseError {
ParseError::new(ErrorCode::ObjectNotFound, "Object not found.")
}