You've already forked lldap
mirror of
https://github.com/lldap/lldap.git
synced 2026-03-31 23:17:48 +01:00
app: Allow custom attributes in group creation
This commit is contained in:
committed by
nitnelave
parent
a190fe7ddf
commit
4ebfd0525b
@@ -1,5 +1,5 @@
|
|||||||
mutation CreateGroup($name: String!) {
|
mutation CreateGroup($group: CreateGroupInput!) {
|
||||||
createGroup(name: $name) {
|
createGroupWithDetails(request: $group) {
|
||||||
id
|
id
|
||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ query GetGroupAttributesSchema {
|
|||||||
isList
|
isList
|
||||||
isVisible
|
isVisible
|
||||||
isHardcoded
|
isHardcoded
|
||||||
|
isReadonly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,23 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{
|
components::{
|
||||||
form::{field::Field, submit::Submit},
|
form::{
|
||||||
|
attribute_input::{ListAttributeInput, SingleAttributeInput},
|
||||||
|
field::Field,
|
||||||
|
submit::Submit,
|
||||||
|
},
|
||||||
router::AppRoute,
|
router::AppRoute,
|
||||||
},
|
},
|
||||||
infra::common_component::{CommonComponent, CommonComponentParts},
|
convert_attribute_type,
|
||||||
|
infra::{
|
||||||
|
common_component::{CommonComponent, CommonComponentParts},
|
||||||
|
form_utils::{
|
||||||
|
read_all_form_attributes, AttributeValue, EmailIsRequired, GraphQlAttributeSchema,
|
||||||
|
IsAdmin,
|
||||||
|
},
|
||||||
|
schema::AttributeType,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{ensure, Result};
|
||||||
use gloo_console::log;
|
use gloo_console::log;
|
||||||
use graphql_client::GraphQLQuery;
|
use graphql_client::GraphQLQuery;
|
||||||
use validator_derive::Validate;
|
use validator_derive::Validate;
|
||||||
@@ -13,6 +25,33 @@ use yew::prelude::*;
|
|||||||
use yew_form_derive::Model;
|
use yew_form_derive::Model;
|
||||||
use yew_router::{prelude::History, scope_ext::RouterScopeExt};
|
use yew_router::{prelude::History, scope_ext::RouterScopeExt};
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "../schema.graphql",
|
||||||
|
query_path = "queries/get_group_attributes_schema.graphql",
|
||||||
|
response_derives = "Debug,Clone,PartialEq,Eq",
|
||||||
|
custom_scalars_module = "crate::infra::graphql"
|
||||||
|
)]
|
||||||
|
pub struct GetGroupAttributesSchema;
|
||||||
|
|
||||||
|
use get_group_attributes_schema::ResponseData;
|
||||||
|
|
||||||
|
pub type Attribute =
|
||||||
|
get_group_attributes_schema::GetGroupAttributesSchemaSchemaGroupSchemaAttributes;
|
||||||
|
|
||||||
|
convert_attribute_type!(get_group_attributes_schema::AttributeType);
|
||||||
|
|
||||||
|
impl From<&Attribute> for GraphQlAttributeSchema {
|
||||||
|
fn from(attr: &Attribute) -> Self {
|
||||||
|
Self {
|
||||||
|
name: attr.name.clone(),
|
||||||
|
is_list: attr.is_list,
|
||||||
|
is_readonly: attr.is_readonly,
|
||||||
|
is_editable: false, // Need to be admin to edit it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(GraphQLQuery)]
|
#[derive(GraphQLQuery)]
|
||||||
#[graphql(
|
#[graphql(
|
||||||
schema_path = "../schema.graphql",
|
schema_path = "../schema.graphql",
|
||||||
@@ -25,6 +64,8 @@ pub struct CreateGroup;
|
|||||||
pub struct CreateGroupForm {
|
pub struct CreateGroupForm {
|
||||||
common: CommonComponentParts<Self>,
|
common: CommonComponentParts<Self>,
|
||||||
form: yew_form::Form<CreateGroupModel>,
|
form: yew_form::Form<CreateGroupModel>,
|
||||||
|
attributes_schema: Option<Vec<Attribute>>,
|
||||||
|
form_ref: NodeRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Model, Validate, PartialEq, Eq, Clone, Default)]
|
#[derive(Model, Validate, PartialEq, Eq, Clone, Default)]
|
||||||
@@ -35,6 +76,7 @@ pub struct CreateGroupModel {
|
|||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
Update,
|
Update,
|
||||||
|
ListAttributesResponse(Result<ResponseData>),
|
||||||
SubmitForm,
|
SubmitForm,
|
||||||
CreateGroupResponse(Result<create_group::ResponseData>),
|
CreateGroupResponse(Result<create_group::ResponseData>),
|
||||||
}
|
}
|
||||||
@@ -48,12 +90,33 @@ impl CommonComponent<CreateGroupForm> for CreateGroupForm {
|
|||||||
match msg {
|
match msg {
|
||||||
Msg::Update => Ok(true),
|
Msg::Update => Ok(true),
|
||||||
Msg::SubmitForm => {
|
Msg::SubmitForm => {
|
||||||
if !self.form.validate() {
|
ensure!(self.form.validate(), "Check the form for errors");
|
||||||
bail!("Check the form for errors");
|
|
||||||
}
|
let all_values = read_all_form_attributes(
|
||||||
|
self.attributes_schema.iter().flatten(),
|
||||||
|
&self.form_ref,
|
||||||
|
IsAdmin(true),
|
||||||
|
EmailIsRequired(false),
|
||||||
|
)?;
|
||||||
|
let attributes = Some(
|
||||||
|
all_values
|
||||||
|
.into_iter()
|
||||||
|
.filter(|a| !a.values.is_empty())
|
||||||
|
.map(
|
||||||
|
|AttributeValue { name, values }| create_group::AttributeValueInput {
|
||||||
|
name,
|
||||||
|
value: values,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
let model = self.form.model();
|
let model = self.form.model();
|
||||||
let req = create_group::Variables {
|
let req = create_group::Variables {
|
||||||
name: model.groupname,
|
group: create_group::CreateGroupInput {
|
||||||
|
displayName: model.groupname,
|
||||||
|
attributes,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
self.common.call_graphql::<CreateGroup, _>(
|
self.common.call_graphql::<CreateGroup, _>(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -66,11 +129,16 @@ impl CommonComponent<CreateGroupForm> for CreateGroupForm {
|
|||||||
Msg::CreateGroupResponse(response) => {
|
Msg::CreateGroupResponse(response) => {
|
||||||
log!(&format!(
|
log!(&format!(
|
||||||
"Created group '{}'",
|
"Created group '{}'",
|
||||||
&response?.create_group.display_name
|
&response?.create_group_with_details.display_name
|
||||||
));
|
));
|
||||||
ctx.link().history().unwrap().push(AppRoute::ListGroups);
|
ctx.link().history().unwrap().push(AppRoute::ListGroups);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
Msg::ListAttributesResponse(schema) => {
|
||||||
|
self.attributes_schema =
|
||||||
|
Some(schema?.schema.group_schema.attributes.into_iter().collect());
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,11 +151,22 @@ impl Component for CreateGroupForm {
|
|||||||
type Message = Msg;
|
type Message = Msg;
|
||||||
type Properties = ();
|
type Properties = ();
|
||||||
|
|
||||||
fn create(_: &Context<Self>) -> Self {
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
Self {
|
let mut component = Self {
|
||||||
common: CommonComponentParts::<Self>::create(),
|
common: CommonComponentParts::<Self>::create(),
|
||||||
form: yew_form::Form::<CreateGroupModel>::new(CreateGroupModel::default()),
|
form: yew_form::Form::<CreateGroupModel>::new(CreateGroupModel::default()),
|
||||||
}
|
attributes_schema: None,
|
||||||
|
form_ref: NodeRef::default(),
|
||||||
|
};
|
||||||
|
component
|
||||||
|
.common
|
||||||
|
.call_graphql::<GetGroupAttributesSchema, _>(
|
||||||
|
ctx,
|
||||||
|
get_group_attributes_schema::Variables {},
|
||||||
|
Msg::ListAttributesResponse,
|
||||||
|
"Error trying to fetch group schema",
|
||||||
|
);
|
||||||
|
component
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
@@ -98,7 +177,8 @@ impl Component for CreateGroupForm {
|
|||||||
let link = ctx.link();
|
let link = ctx.link();
|
||||||
html! {
|
html! {
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<form class="form py-3" style="max-width: 636px">
|
<form class="form py-3" style="max-width: 636px"
|
||||||
|
ref={self.form_ref.clone()}>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<h5 class="fw-bold">{"Create a group"}</h5>
|
<h5 class="fw-bold">{"Create a group"}</h5>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,6 +188,14 @@ impl Component for CreateGroupForm {
|
|||||||
label="Group name"
|
label="Group name"
|
||||||
field_name="groupname"
|
field_name="groupname"
|
||||||
oninput={link.callback(|_| Msg::Update)} />
|
oninput={link.callback(|_| Msg::Update)} />
|
||||||
|
{
|
||||||
|
self.attributes_schema
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|a| !a.is_readonly && a.name != "display_name")
|
||||||
|
.map(get_custom_attribute_input)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
<Submit
|
<Submit
|
||||||
disabled={self.common.is_task_running()}
|
disabled={self.common.is_task_running()}
|
||||||
onclick={link.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitForm})} />
|
onclick={link.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitForm})} />
|
||||||
@@ -124,3 +212,21 @@ impl Component for CreateGroupForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_custom_attribute_input(attribute_schema: &Attribute) -> Html {
|
||||||
|
if attribute_schema.is_list {
|
||||||
|
html! {
|
||||||
|
<ListAttributeInput
|
||||||
|
name={attribute_schema.name.clone()}
|
||||||
|
attribute_type={Into::<AttributeType>::into(attribute_schema.attribute_type.clone())}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
<SingleAttributeInput
|
||||||
|
name={attribute_schema.name.clone()}
|
||||||
|
attribute_type={Into::<AttributeType>::into(attribute_schema.attribute_type.clone())}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ use crate::{
|
|||||||
infra::{
|
infra::{
|
||||||
api::HostService,
|
api::HostService,
|
||||||
common_component::{CommonComponent, CommonComponentParts},
|
common_component::{CommonComponent, CommonComponentParts},
|
||||||
form_utils::{read_all_form_attributes, AttributeValue, GraphQlAttributeSchema},
|
form_utils::{
|
||||||
|
read_all_form_attributes, AttributeValue, EmailIsRequired, GraphQlAttributeSchema,
|
||||||
|
IsAdmin,
|
||||||
|
},
|
||||||
schema::AttributeType,
|
schema::AttributeType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -121,8 +124,8 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
|
|||||||
let all_values = read_all_form_attributes(
|
let all_values = read_all_form_attributes(
|
||||||
self.attributes_schema.iter().flatten(),
|
self.attributes_schema.iter().flatten(),
|
||||||
&self.form_ref,
|
&self.form_ref,
|
||||||
true,
|
IsAdmin(true),
|
||||||
true,
|
EmailIsRequired(true),
|
||||||
)?;
|
)?;
|
||||||
let attributes = Some(
|
let attributes = Some(
|
||||||
all_values
|
all_values
|
||||||
@@ -302,7 +305,7 @@ impl Component for CreateUserForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_custom_attribute_input(attribute_schema: &Attribute) -> Html {
|
fn get_custom_attribute_input(attribute_schema: &Attribute) -> Html {
|
||||||
if attribute_schema.is_list {
|
if attribute_schema.is_list {
|
||||||
html! {
|
html! {
|
||||||
<ListAttributeInput
|
<ListAttributeInput
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
common_component::{CommonComponent, CommonComponentParts},
|
common_component::{CommonComponent, CommonComponentParts},
|
||||||
form_utils::{read_all_form_attributes, AttributeValue},
|
form_utils::{read_all_form_attributes, AttributeValue, EmailIsRequired, IsAdmin},
|
||||||
schema::AttributeType,
|
schema::AttributeType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -210,8 +210,8 @@ impl GroupDetailsForm {
|
|||||||
let mut all_values = read_all_form_attributes(
|
let mut all_values = read_all_form_attributes(
|
||||||
ctx.props().group_attributes_schema.iter(),
|
ctx.props().group_attributes_schema.iter(),
|
||||||
&self.form_ref,
|
&self.form_ref,
|
||||||
ctx.props().is_admin,
|
IsAdmin(ctx.props().is_admin),
|
||||||
false,
|
EmailIsRequired(false),
|
||||||
)?;
|
)?;
|
||||||
let base_attributes = &self.group.attributes;
|
let base_attributes = &self.group.attributes;
|
||||||
all_values.retain(|a| {
|
all_values.retain(|a| {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
common_component::{CommonComponent, CommonComponentParts},
|
common_component::{CommonComponent, CommonComponentParts},
|
||||||
form_utils::{read_all_form_attributes, AttributeValue},
|
form_utils::{read_all_form_attributes, AttributeValue, EmailIsRequired, IsAdmin},
|
||||||
schema::AttributeType,
|
schema::AttributeType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -212,8 +212,8 @@ impl UserDetailsForm {
|
|||||||
let mut all_values = read_all_form_attributes(
|
let mut all_values = read_all_form_attributes(
|
||||||
ctx.props().user_attributes_schema.iter(),
|
ctx.props().user_attributes_schema.iter(),
|
||||||
&self.form_ref,
|
&self.form_ref,
|
||||||
ctx.props().is_admin,
|
IsAdmin(ctx.props().is_admin),
|
||||||
!ctx.props().is_edited_user_admin,
|
EmailIsRequired(!ctx.props().is_edited_user_admin),
|
||||||
)?;
|
)?;
|
||||||
let base_attributes = &self.user.attributes;
|
let base_attributes = &self.user.attributes;
|
||||||
all_values.retain(|a| {
|
all_values.retain(|a| {
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ pub struct GraphQlAttributeSchema {
|
|||||||
pub is_editable: bool,
|
pub is_editable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_attributes(all_values: &[AttributeValue], email_is_required: bool) -> Result<()> {
|
fn validate_attributes(
|
||||||
|
all_values: &[AttributeValue],
|
||||||
|
email_is_required: EmailIsRequired,
|
||||||
|
) -> Result<()> {
|
||||||
let maybe_email_values = all_values.iter().find(|a| a.name == "mail");
|
let maybe_email_values = all_values.iter().find(|a| a.name == "mail");
|
||||||
if email_is_required || maybe_email_values.is_some() {
|
if email_is_required.0 || maybe_email_values.is_some() {
|
||||||
let email_values = &maybe_email_values
|
let email_values = &maybe_email_values
|
||||||
.ok_or_else(|| anyhow!("Email is required"))?
|
.ok_or_else(|| anyhow!("Email is required"))?
|
||||||
.values;
|
.values;
|
||||||
@@ -28,11 +31,14 @@ fn validate_attributes(all_values: &[AttributeValue], email_is_required: bool) -
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct IsAdmin(pub bool);
|
||||||
|
pub struct EmailIsRequired(pub bool);
|
||||||
|
|
||||||
pub fn read_all_form_attributes(
|
pub fn read_all_form_attributes(
|
||||||
schema: impl IntoIterator<Item = impl Into<GraphQlAttributeSchema>>,
|
schema: impl IntoIterator<Item = impl Into<GraphQlAttributeSchema>>,
|
||||||
form_ref: &NodeRef,
|
form_ref: &NodeRef,
|
||||||
is_admin: bool,
|
is_admin: IsAdmin,
|
||||||
email_is_required: bool,
|
email_is_required: EmailIsRequired,
|
||||||
) -> Result<Vec<AttributeValue>> {
|
) -> Result<Vec<AttributeValue>> {
|
||||||
let form = form_ref.cast::<HtmlFormElement>().unwrap();
|
let form = form_ref.cast::<HtmlFormElement>().unwrap();
|
||||||
let form_data = FormData::new_with_form(&form)
|
let form_data = FormData::new_with_form(&form)
|
||||||
@@ -40,7 +46,7 @@ pub fn read_all_form_attributes(
|
|||||||
let all_values = schema
|
let all_values = schema
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Into::<GraphQlAttributeSchema>::into)
|
.map(Into::<GraphQlAttributeSchema>::into)
|
||||||
.filter(|attr| !attr.is_readonly && (is_admin || attr.is_editable))
|
.filter(|attr| !attr.is_readonly && (is_admin.0 || attr.is_editable))
|
||||||
.map(|attr| -> Result<AttributeValue> {
|
.map(|attr| -> Result<AttributeValue> {
|
||||||
let val = form_data
|
let val = form_data
|
||||||
.get_all(attr.name.as_str())
|
.get_all(attr.name.as_str())
|
||||||
|
|||||||
Reference in New Issue
Block a user