openauth-plugins 0.0.4

Official OpenAuth plugin modules.
Documentation
use std::collections::BTreeMap;

use openauth_core::context::AuthContext;
use openauth_core::db::{DbAdapter, DbRecord, DbValue, FindOne, User, Where};
use openauth_core::error::OpenAuthError;
use openauth_core::options::UserAdditionalField;
use serde_json::{Map, Value};

pub(crate) fn additional_user_create_values(context: &AuthContext) -> DbRecord {
    context
        .options
        .user
        .additional_fields
        .iter()
        .map(|(name, field)| {
            (
                name.clone(),
                field.default_value.clone().unwrap_or(DbValue::Null),
            )
        })
        .collect()
}

pub(crate) async fn user_response_value(
    adapter: &dyn DbAdapter,
    context: &AuthContext,
    user: &User,
) -> Result<Value, OpenAuthError> {
    let mut value =
        serde_json::to_value(user).map_err(|error| OpenAuthError::Api(error.to_string()))?;
    let Value::Object(object) = &mut value else {
        return Ok(value);
    };
    let record = adapter
        .find_one(
            FindOne::new("user").where_clause(Where::new("id", DbValue::String(user.id.clone()))),
        )
        .await?;
    insert_returned_user_fields(
        object,
        &context.options.user.additional_fields,
        record.as_ref(),
    )?;
    Ok(value)
}

fn insert_returned_user_fields(
    object: &mut Map<String, Value>,
    fields: &BTreeMap<String, UserAdditionalField>,
    record: Option<&DbRecord>,
) -> Result<(), OpenAuthError> {
    for (name, field) in fields {
        if !field.returned {
            continue;
        }
        let value = record
            .and_then(|record| record.get(name))
            .or(field.default_value.as_ref())
            .unwrap_or(&DbValue::Null);
        object.insert(name.clone(), db_value_to_json(value)?);
    }
    Ok(())
}

fn db_value_to_json(value: &DbValue) -> Result<Value, OpenAuthError> {
    match value {
        DbValue::String(value) => Ok(Value::String(value.clone())),
        DbValue::Number(value) => Ok(Value::Number((*value).into())),
        DbValue::Boolean(value) => Ok(Value::Bool(*value)),
        DbValue::Timestamp(value) => {
            serde_json::to_value(value).map_err(|error| OpenAuthError::Api(error.to_string()))
        }
        DbValue::Json(value) => Ok(value.clone()),
        DbValue::StringArray(values) => Ok(Value::Array(
            values.iter().cloned().map(Value::String).collect(),
        )),
        DbValue::NumberArray(values) => Ok(Value::Array(
            values
                .iter()
                .map(|value| Value::Number((*value).into()))
                .collect(),
        )),
        DbValue::Record(record) => db_record_to_json(record),
        DbValue::RecordArray(records) => records
            .iter()
            .map(db_record_to_json)
            .collect::<Result<Vec<_>, _>>()
            .map(Value::Array),
        DbValue::Null => Ok(Value::Null),
    }
}

fn db_record_to_json(record: &DbRecord) -> Result<Value, OpenAuthError> {
    record
        .iter()
        .map(|(field, value)| db_value_to_json(value).map(|value| (field.clone(), value)))
        .collect::<Result<Map<_, _>, _>>()
        .map(Value::Object)
}