mirror of
https://github.com/lldap/lldap.git
synced 2024-11-25 09:06:03 +00:00
server: Add the attribute schema to the attributes in graphql
And make sure that we only request the schema once per top-level query
This commit is contained in:
parent
1f2f034a48
commit
e308a5e9a1
4 changed files with 229 additions and 181 deletions
9
schema.graphql
generated
9
schema.graphql
generated
|
@ -1,6 +1,7 @@
|
|||
type AttributeValue {
|
||||
name: String!
|
||||
value: [String!]!
|
||||
schema: AttributeSchema!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
@ -152,10 +153,6 @@ type User {
|
|||
groups: [Group!]!
|
||||
}
|
||||
|
||||
type AttributeList {
|
||||
attributes: [AttributeSchema!]!
|
||||
}
|
||||
|
||||
enum AttributeType {
|
||||
STRING
|
||||
INTEGER
|
||||
|
@ -163,6 +160,10 @@ enum AttributeType {
|
|||
DATE_TIME
|
||||
}
|
||||
|
||||
type AttributeList {
|
||||
attributes: [AttributeSchema!]!
|
||||
}
|
||||
|
||||
type Success {
|
||||
ok: Boolean!
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::domain::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct PublicSchema(Schema);
|
||||
|
||||
impl PublicSchema {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
domain::{
|
||||
deserialize::deserialize_attribute_value,
|
||||
|
@ -159,11 +161,8 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||
})
|
||||
.instrument(span.clone())
|
||||
.await?;
|
||||
Ok(handler
|
||||
.get_user_details(&user_id)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(Into::into)?)
|
||||
let user_details = handler.get_user_details(&user_id).instrument(span).await?;
|
||||
super::query::User::<Handler>::from_user(user_details, Arc::new(schema))
|
||||
}
|
||||
|
||||
async fn create_group(
|
||||
|
@ -513,11 +512,8 @@ async fn create_group_with_details<Handler: BackendHandler>(
|
|||
attributes,
|
||||
};
|
||||
let group_id = handler.create_group(request).await?;
|
||||
Ok(handler
|
||||
.get_group_details(group_id)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(Into::into)?)
|
||||
let group_details = handler.get_group_details(group_id).instrument(span).await?;
|
||||
super::query::Group::<Handler>::from_group_details(group_details, Arc::new(schema))
|
||||
}
|
||||
|
||||
fn deserialize_attribute(
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
domain::{
|
||||
deserialize::deserialize_attribute_value,
|
||||
handler::{BackendHandler, ReadSchemaBackendHandler},
|
||||
ldap::utils::{map_user_field, UserFieldType},
|
||||
model::UserColumn,
|
||||
schema::{
|
||||
PublicSchema, SchemaAttributeExtractor, SchemaGroupAttributeExtractor,
|
||||
SchemaUserAttributeExtractor,
|
||||
},
|
||||
schema::PublicSchema,
|
||||
types::{AttributeType, GroupDetails, GroupId, JpegPhoto, UserId},
|
||||
},
|
||||
infra::{
|
||||
|
@ -143,11 +142,9 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
&span,
|
||||
"Unauthorized access to user data",
|
||||
))?;
|
||||
Ok(handler
|
||||
.get_user_details(&user_id)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(Into::into)?)
|
||||
let schema = Arc::new(self.get_schema(context, span.clone()).await?);
|
||||
let user = handler.get_user_details(&user_id).instrument(span).await?;
|
||||
User::<Handler>::from_user(user, schema)
|
||||
}
|
||||
|
||||
async fn users(
|
||||
|
@ -164,8 +161,8 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
&span,
|
||||
"Unauthorized access to user list",
|
||||
))?;
|
||||
let schema = self.get_schema(context, span.clone()).await?;
|
||||
Ok(handler
|
||||
let schema = Arc::new(self.get_schema(context, span.clone()).await?);
|
||||
let users = handler
|
||||
.list_users(
|
||||
filters
|
||||
.map(|f| f.try_into_domain_filter(&schema))
|
||||
|
@ -173,8 +170,11 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
false,
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(|v| v.into_iter().map(Into::into).collect())?)
|
||||
.await?;
|
||||
users
|
||||
.into_iter()
|
||||
.map(|u| User::<Handler>::from_user_and_groups(u, schema.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn groups(context: &Context<Handler>) -> FieldResult<Vec<Group<Handler>>> {
|
||||
|
@ -185,11 +185,12 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
&span,
|
||||
"Unauthorized access to group list",
|
||||
))?;
|
||||
Ok(handler
|
||||
.list_groups(None)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(|v| v.into_iter().map(Into::into).collect())?)
|
||||
let schema = Arc::new(self.get_schema(context, span.clone()).await?);
|
||||
let domain_groups = handler.list_groups(None).instrument(span).await?;
|
||||
domain_groups
|
||||
.into_iter()
|
||||
.map(|g| Group::<Handler>::from_group(g, schema.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn group(context: &Context<Handler>, group_id: i32) -> FieldResult<Group<Handler>> {
|
||||
|
@ -203,11 +204,12 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
&span,
|
||||
"Unauthorized access to group data",
|
||||
))?;
|
||||
Ok(handler
|
||||
let schema = Arc::new(self.get_schema(context, span.clone()).await?);
|
||||
let group_details = handler
|
||||
.get_group_details(GroupId(group_id))
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(Into::into)?)
|
||||
.await?;
|
||||
Group::<Handler>::from_group_details(group_details, schema.clone())
|
||||
}
|
||||
|
||||
async fn schema(context: &Context<Handler>) -> FieldResult<Schema<Handler>> {
|
||||
|
@ -237,16 +239,45 @@ impl<Handler: BackendHandler> Query<Handler> {
|
|||
/// Represents a single user.
|
||||
pub struct User<Handler: BackendHandler> {
|
||||
user: DomainUser,
|
||||
attributes: Vec<AttributeValue<Handler>>,
|
||||
schema: Arc<PublicSchema>,
|
||||
groups: Option<Vec<Group<Handler>>>,
|
||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<Handler: BackendHandler> Default for User<Handler> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
user: DomainUser::default(),
|
||||
impl<Handler: BackendHandler> User<Handler> {
|
||||
pub fn from_user(mut user: DomainUser, schema: Arc<PublicSchema>) -> FieldResult<Self> {
|
||||
let attributes = std::mem::take(&mut user.attributes);
|
||||
Ok(Self {
|
||||
user,
|
||||
attributes: attributes
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
AttributeValue::<Handler>::from_schema(a, &schema.get_schema().user_attributes)
|
||||
})
|
||||
.collect::<FieldResult<Vec<_>>>()?,
|
||||
schema,
|
||||
groups: None,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> User<Handler> {
|
||||
pub fn from_user_and_groups(
|
||||
DomainUserAndGroups { user, groups }: DomainUserAndGroups,
|
||||
schema: Arc<PublicSchema>,
|
||||
) -> FieldResult<Self> {
|
||||
let mut user = Self::from_user(user, schema.clone())?;
|
||||
if let Some(groups) = groups {
|
||||
user.groups = Some(
|
||||
groups
|
||||
.into_iter()
|
||||
.map(|g| Group::<Handler>::from_group_details(g, schema.clone()))
|
||||
.collect::<FieldResult<Vec<_>>>()?,
|
||||
);
|
||||
}
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,17 +330,15 @@ impl<Handler: BackendHandler> User<Handler> {
|
|||
}
|
||||
|
||||
/// User-defined attributes.
|
||||
fn attributes(&self) -> Vec<AttributeValue<Handler, SchemaUserAttributeExtractor>> {
|
||||
self.user
|
||||
.attributes
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
fn attributes(&self) -> &[AttributeValue<Handler>] {
|
||||
&self.attributes
|
||||
}
|
||||
|
||||
/// The groups to which this user belongs.
|
||||
async fn groups(&self, context: &Context<Handler>) -> FieldResult<Vec<Group<Handler>>> {
|
||||
if let Some(groups) = &self.groups {
|
||||
return Ok(groups.clone());
|
||||
}
|
||||
let span = debug_span!("[GraphQL query] user::groups");
|
||||
span.in_scope(|| {
|
||||
debug!(user_id = ?self.user.user_id);
|
||||
|
@ -317,36 +346,16 @@ impl<Handler: BackendHandler> User<Handler> {
|
|||
let handler = context
|
||||
.get_readable_handler(&self.user.user_id)
|
||||
.expect("We shouldn't be able to get there without readable permission");
|
||||
Ok(handler
|
||||
let domain_groups = handler
|
||||
.get_user_groups(&self.user.user_id)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(|set| {
|
||||
let mut groups = set
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<Group<Handler>>>();
|
||||
groups.sort_by(|g1, g2| g1.display_name.cmp(&g2.display_name));
|
||||
groups
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> From<DomainUser> for User<Handler> {
|
||||
fn from(user: DomainUser) -> Self {
|
||||
Self {
|
||||
user,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> From<DomainUserAndGroups> for User<Handler> {
|
||||
fn from(user: DomainUserAndGroups) -> Self {
|
||||
Self {
|
||||
user: user.user,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
.await?;
|
||||
let mut groups = domain_groups
|
||||
.into_iter()
|
||||
.map(|g| Group::<Handler>::from_group_details(g, self.schema.clone()))
|
||||
.collect::<FieldResult<Vec<Group<Handler>>>>()?;
|
||||
groups.sort_by(|g1, g2| g1.display_name.cmp(&g2.display_name));
|
||||
Ok(groups)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,11 +366,69 @@ pub struct Group<Handler: BackendHandler> {
|
|||
display_name: String,
|
||||
creation_date: chrono::NaiveDateTime,
|
||||
uuid: String,
|
||||
attributes: Vec<DomainAttributeValue>,
|
||||
members: Option<Vec<String>>,
|
||||
attributes: Vec<AttributeValue<Handler>>,
|
||||
schema: Arc<PublicSchema>,
|
||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> Group<Handler> {
|
||||
pub fn from_group(
|
||||
group: DomainGroup,
|
||||
schema: Arc<PublicSchema>,
|
||||
) -> FieldResult<Group<Handler>> {
|
||||
Ok(Self {
|
||||
group_id: group.id.0,
|
||||
display_name: group.display_name.to_string(),
|
||||
creation_date: group.creation_date,
|
||||
uuid: group.uuid.into_string(),
|
||||
attributes: group
|
||||
.attributes
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
AttributeValue::<Handler>::from_schema(a, &schema.get_schema().group_attributes)
|
||||
})
|
||||
.collect::<FieldResult<Vec<_>>>()?,
|
||||
schema,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_group_details(
|
||||
group_details: GroupDetails,
|
||||
schema: Arc<PublicSchema>,
|
||||
) -> FieldResult<Group<Handler>> {
|
||||
Ok(Self {
|
||||
group_id: group_details.group_id.0,
|
||||
display_name: group_details.display_name.to_string(),
|
||||
creation_date: group_details.creation_date,
|
||||
uuid: group_details.uuid.into_string(),
|
||||
attributes: group_details
|
||||
.attributes
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
AttributeValue::<Handler>::from_schema(a, &schema.get_schema().group_attributes)
|
||||
})
|
||||
.collect::<FieldResult<Vec<_>>>()?,
|
||||
schema,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> Clone for Group<Handler> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
group_id: self.group_id,
|
||||
display_name: self.display_name.clone(),
|
||||
creation_date: self.creation_date,
|
||||
uuid: self.uuid.clone(),
|
||||
attributes: self.attributes.clone(),
|
||||
schema: self.schema.clone(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql_object(context = Context<Handler>)]
|
||||
impl<Handler: BackendHandler> Group<Handler> {
|
||||
fn id(&self) -> i32 {
|
||||
|
@ -378,12 +445,8 @@ impl<Handler: BackendHandler> Group<Handler> {
|
|||
}
|
||||
|
||||
/// User-defined attributes.
|
||||
fn attributes(&self) -> Vec<AttributeValue<Handler, SchemaGroupAttributeExtractor>> {
|
||||
self.attributes
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
fn attributes(&self) -> &[AttributeValue<Handler>] {
|
||||
&self.attributes
|
||||
}
|
||||
|
||||
/// The groups to which this user belongs.
|
||||
|
@ -398,42 +461,17 @@ impl<Handler: BackendHandler> Group<Handler> {
|
|||
&span,
|
||||
"Unauthorized access to group data",
|
||||
))?;
|
||||
Ok(handler
|
||||
let domain_users = handler
|
||||
.list_users(
|
||||
Some(DomainRequestFilter::MemberOfId(GroupId(self.group_id))),
|
||||
false,
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
.map(|v| v.into_iter().map(Into::into).collect())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> From<GroupDetails> for Group<Handler> {
|
||||
fn from(group_details: GroupDetails) -> Self {
|
||||
Self {
|
||||
group_id: group_details.group_id.0,
|
||||
display_name: group_details.display_name.to_string(),
|
||||
creation_date: group_details.creation_date,
|
||||
uuid: group_details.uuid.into_string(),
|
||||
attributes: group_details.attributes,
|
||||
members: None,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> From<DomainGroup> for Group<Handler> {
|
||||
fn from(group: DomainGroup) -> Self {
|
||||
Self {
|
||||
group_id: group.id.0,
|
||||
display_name: group.display_name.to_string(),
|
||||
creation_date: group.creation_date,
|
||||
uuid: group.uuid.into_string(),
|
||||
attributes: group.attributes,
|
||||
members: Some(group.users.into_iter().map(UserId::into_string).collect()),
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
.await?;
|
||||
domain_users
|
||||
.into_iter()
|
||||
.map(|u| User::<Handler>::from_user_and_groups(u, self.schema.clone()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,6 +503,15 @@ impl<Handler: BackendHandler> AttributeSchema<Handler> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> Clone for AttributeSchema<Handler> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
schema: self.schema.clone(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> From<DomainAttributeSchema> for AttributeSchema<Handler> {
|
||||
fn from(value: DomainAttributeSchema) -> Self {
|
||||
Self {
|
||||
|
@ -527,88 +574,92 @@ impl<Handler: BackendHandler> From<PublicSchema> for Schema<Handler> {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
pub struct AttributeValue<Handler: BackendHandler, Extractor> {
|
||||
pub struct AttributeValue<Handler: BackendHandler> {
|
||||
attribute: DomainAttributeValue,
|
||||
schema: AttributeSchema<Handler>,
|
||||
_phantom: std::marker::PhantomData<Box<Handler>>,
|
||||
_phantom_extractor: std::marker::PhantomData<Extractor>,
|
||||
}
|
||||
|
||||
#[graphql_object(context = Context<Handler>)]
|
||||
impl<Handler: BackendHandler, Extractor: SchemaAttributeExtractor>
|
||||
AttributeValue<Handler, Extractor>
|
||||
{
|
||||
impl<Handler: BackendHandler> AttributeValue<Handler> {
|
||||
fn name(&self) -> &str {
|
||||
self.attribute.name.as_str()
|
||||
}
|
||||
async fn value(&self, context: &Context<Handler>) -> FieldResult<Vec<String>> {
|
||||
let handler = context
|
||||
.handler
|
||||
.get_user_restricted_lister_handler(&context.validation_result);
|
||||
serialize_attribute(
|
||||
&self.attribute,
|
||||
Extractor::get_attributes(&PublicSchema::from(handler.get_schema().await?)),
|
||||
)
|
||||
|
||||
fn value(&self) -> FieldResult<Vec<String>> {
|
||||
Ok(serialize_attribute(&self.attribute, &self.schema.schema))
|
||||
}
|
||||
|
||||
fn schema(&self) -> &AttributeSchema<Handler> {
|
||||
&self.schema
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler> Clone for AttributeValue<Handler> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
attribute: self.attribute.clone(),
|
||||
schema: self.schema.clone(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_attribute(
|
||||
attribute: &DomainAttributeValue,
|
||||
attributes: &DomainAttributeList,
|
||||
) -> FieldResult<Vec<String>> {
|
||||
attribute_schema: &DomainAttributeSchema,
|
||||
) -> Vec<String> {
|
||||
let convert_date = |date| chrono::Utc.from_utc_datetime(&date).to_rfc3339();
|
||||
attributes
|
||||
.get_attribute_type(&attribute.name)
|
||||
.map(|attribute_type| {
|
||||
match attribute_type {
|
||||
(AttributeType::String, false) => {
|
||||
vec![attribute.value.unwrap::<String>()]
|
||||
}
|
||||
(AttributeType::Integer, false) => {
|
||||
// LDAP integers are encoded as strings.
|
||||
vec![attribute.value.unwrap::<i64>().to_string()]
|
||||
}
|
||||
(AttributeType::JpegPhoto, false) => {
|
||||
vec![String::from(&attribute.value.unwrap::<JpegPhoto>())]
|
||||
}
|
||||
(AttributeType::DateTime, false) => {
|
||||
vec![convert_date(attribute.value.unwrap::<NaiveDateTime>())]
|
||||
}
|
||||
(AttributeType::String, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<String>>()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
(AttributeType::Integer, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<i64>>()
|
||||
.into_iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect(),
|
||||
(AttributeType::JpegPhoto, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<JpegPhoto>>()
|
||||
.iter()
|
||||
.map(String::from)
|
||||
.collect(),
|
||||
(AttributeType::DateTime, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<NaiveDateTime>>()
|
||||
.into_iter()
|
||||
.map(convert_date)
|
||||
.collect(),
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| FieldError::from(anyhow::anyhow!("Unknown attribute: {}", &attribute.name)))
|
||||
match (attribute_schema.attribute_type, attribute_schema.is_list) {
|
||||
(AttributeType::String, false) => vec![attribute.value.unwrap::<String>()],
|
||||
(AttributeType::Integer, false) => {
|
||||
// LDAP integers are encoded as strings.
|
||||
vec![attribute.value.unwrap::<i64>().to_string()]
|
||||
}
|
||||
(AttributeType::JpegPhoto, false) => {
|
||||
vec![String::from(&attribute.value.unwrap::<JpegPhoto>())]
|
||||
}
|
||||
(AttributeType::DateTime, false) => {
|
||||
vec![convert_date(attribute.value.unwrap::<NaiveDateTime>())]
|
||||
}
|
||||
(AttributeType::String, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<String>>()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
(AttributeType::Integer, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<i64>>()
|
||||
.into_iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect(),
|
||||
(AttributeType::JpegPhoto, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<JpegPhoto>>()
|
||||
.iter()
|
||||
.map(String::from)
|
||||
.collect(),
|
||||
(AttributeType::DateTime, true) => attribute
|
||||
.value
|
||||
.unwrap::<Vec<NaiveDateTime>>()
|
||||
.into_iter()
|
||||
.map(convert_date)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: BackendHandler, Extractor> From<DomainAttributeValue>
|
||||
for AttributeValue<Handler, Extractor>
|
||||
{
|
||||
fn from(value: DomainAttributeValue) -> Self {
|
||||
Self {
|
||||
attribute: value,
|
||||
_phantom: std::marker::PhantomData,
|
||||
_phantom_extractor: std::marker::PhantomData,
|
||||
impl<Handler: BackendHandler> AttributeValue<Handler> {
|
||||
fn from_schema(a: DomainAttributeValue, schema: &DomainAttributeList) -> FieldResult<Self> {
|
||||
match schema.get_attribute_schema(&a.name) {
|
||||
Some(s) => Ok(AttributeValue::<Handler> {
|
||||
attribute: a,
|
||||
schema: AttributeSchema::<Handler> {
|
||||
schema: s.clone(),
|
||||
_phantom: std::marker::PhantomData,
|
||||
},
|
||||
_phantom: std::marker::PhantomData,
|
||||
}),
|
||||
None => Err(FieldError::from(format!("Unknown attribute {}", &a.name))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue