mirror of
https://github.com/lldap/lldap.git
synced 2024-11-25 09:06:03 +00:00
app: Allow custom attributes in group creation
This commit is contained in:
parent
a190fe7ddf
commit
4ebfd0525b
7 changed files with 145 additions and 29 deletions
|
@ -1,5 +1,5 @@
|
|||
mutation CreateGroup($name: String!) {
|
||||
createGroup(name: $name) {
|
||||
mutation CreateGroup($group: CreateGroupInput!) {
|
||||
createGroupWithDetails(request: $group) {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ query GetGroupAttributesSchema {
|
|||
isList
|
||||
isVisible
|
||||
isHardcoded
|
||||
isReadonly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
use crate::{
|
||||
components::{
|
||||
form::{field::Field, submit::Submit},
|
||||
form::{
|
||||
attribute_input::{ListAttributeInput, SingleAttributeInput},
|
||||
field::Field,
|
||||
submit::Submit,
|
||||
},
|
||||
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 graphql_client::GraphQLQuery;
|
||||
use validator_derive::Validate;
|
||||
|
@ -13,6 +25,33 @@ use yew::prelude::*;
|
|||
use yew_form_derive::Model;
|
||||
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)]
|
||||
#[graphql(
|
||||
schema_path = "../schema.graphql",
|
||||
|
@ -25,6 +64,8 @@ pub struct CreateGroup;
|
|||
pub struct CreateGroupForm {
|
||||
common: CommonComponentParts<Self>,
|
||||
form: yew_form::Form<CreateGroupModel>,
|
||||
attributes_schema: Option<Vec<Attribute>>,
|
||||
form_ref: NodeRef,
|
||||
}
|
||||
|
||||
#[derive(Model, Validate, PartialEq, Eq, Clone, Default)]
|
||||
|
@ -35,6 +76,7 @@ pub struct CreateGroupModel {
|
|||
|
||||
pub enum Msg {
|
||||
Update,
|
||||
ListAttributesResponse(Result<ResponseData>),
|
||||
SubmitForm,
|
||||
CreateGroupResponse(Result<create_group::ResponseData>),
|
||||
}
|
||||
|
@ -48,12 +90,33 @@ impl CommonComponent<CreateGroupForm> for CreateGroupForm {
|
|||
match msg {
|
||||
Msg::Update => Ok(true),
|
||||
Msg::SubmitForm => {
|
||||
if !self.form.validate() {
|
||||
bail!("Check the form for errors");
|
||||
}
|
||||
ensure!(self.form.validate(), "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 req = create_group::Variables {
|
||||
name: model.groupname,
|
||||
group: create_group::CreateGroupInput {
|
||||
displayName: model.groupname,
|
||||
attributes,
|
||||
},
|
||||
};
|
||||
self.common.call_graphql::<CreateGroup, _>(
|
||||
ctx,
|
||||
|
@ -66,11 +129,16 @@ impl CommonComponent<CreateGroupForm> for CreateGroupForm {
|
|||
Msg::CreateGroupResponse(response) => {
|
||||
log!(&format!(
|
||||
"Created group '{}'",
|
||||
&response?.create_group.display_name
|
||||
&response?.create_group_with_details.display_name
|
||||
));
|
||||
ctx.link().history().unwrap().push(AppRoute::ListGroups);
|
||||
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 Properties = ();
|
||||
|
||||
fn create(_: &Context<Self>) -> Self {
|
||||
Self {
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
let mut component = Self {
|
||||
common: CommonComponentParts::<Self>::create(),
|
||||
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 {
|
||||
|
@ -98,7 +177,8 @@ impl Component for CreateGroupForm {
|
|||
let link = ctx.link();
|
||||
html! {
|
||||
<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">
|
||||
<h5 class="fw-bold">{"Create a group"}</h5>
|
||||
</div>
|
||||
|
@ -108,6 +188,14 @@ impl Component for CreateGroupForm {
|
|||
label="Group name"
|
||||
field_name="groupname"
|
||||
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
|
||||
disabled={self.common.is_task_running()}
|
||||
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::{
|
||||
api::HostService,
|
||||
common_component::{CommonComponent, CommonComponentParts},
|
||||
form_utils::{read_all_form_attributes, AttributeValue, GraphQlAttributeSchema},
|
||||
form_utils::{
|
||||
read_all_form_attributes, AttributeValue, EmailIsRequired, GraphQlAttributeSchema,
|
||||
IsAdmin,
|
||||
},
|
||||
schema::AttributeType,
|
||||
},
|
||||
};
|
||||
|
@ -121,8 +124,8 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
|
|||
let all_values = read_all_form_attributes(
|
||||
self.attributes_schema.iter().flatten(),
|
||||
&self.form_ref,
|
||||
true,
|
||||
true,
|
||||
IsAdmin(true),
|
||||
EmailIsRequired(true),
|
||||
)?;
|
||||
let attributes = Some(
|
||||
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 {
|
||||
html! {
|
||||
<ListAttributeInput
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
},
|
||||
infra::{
|
||||
common_component::{CommonComponent, CommonComponentParts},
|
||||
form_utils::{read_all_form_attributes, AttributeValue},
|
||||
form_utils::{read_all_form_attributes, AttributeValue, EmailIsRequired, IsAdmin},
|
||||
schema::AttributeType,
|
||||
},
|
||||
};
|
||||
|
@ -210,8 +210,8 @@ impl GroupDetailsForm {
|
|||
let mut all_values = read_all_form_attributes(
|
||||
ctx.props().group_attributes_schema.iter(),
|
||||
&self.form_ref,
|
||||
ctx.props().is_admin,
|
||||
false,
|
||||
IsAdmin(ctx.props().is_admin),
|
||||
EmailIsRequired(false),
|
||||
)?;
|
||||
let base_attributes = &self.group.attributes;
|
||||
all_values.retain(|a| {
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
},
|
||||
infra::{
|
||||
common_component::{CommonComponent, CommonComponentParts},
|
||||
form_utils::{read_all_form_attributes, AttributeValue},
|
||||
form_utils::{read_all_form_attributes, AttributeValue, EmailIsRequired, IsAdmin},
|
||||
schema::AttributeType,
|
||||
},
|
||||
};
|
||||
|
@ -212,8 +212,8 @@ impl UserDetailsForm {
|
|||
let mut all_values = read_all_form_attributes(
|
||||
ctx.props().user_attributes_schema.iter(),
|
||||
&self.form_ref,
|
||||
ctx.props().is_admin,
|
||||
!ctx.props().is_edited_user_admin,
|
||||
IsAdmin(ctx.props().is_admin),
|
||||
EmailIsRequired(!ctx.props().is_edited_user_admin),
|
||||
)?;
|
||||
let base_attributes = &self.user.attributes;
|
||||
all_values.retain(|a| {
|
||||
|
|
|
@ -16,9 +16,12 @@ pub struct GraphQlAttributeSchema {
|
|||
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");
|
||||
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
|
||||
.ok_or_else(|| anyhow!("Email is required"))?
|
||||
.values;
|
||||
|
@ -28,11 +31,14 @@ fn validate_attributes(all_values: &[AttributeValue], email_is_required: bool) -
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub struct IsAdmin(pub bool);
|
||||
pub struct EmailIsRequired(pub bool);
|
||||
|
||||
pub fn read_all_form_attributes(
|
||||
schema: impl IntoIterator<Item = impl Into<GraphQlAttributeSchema>>,
|
||||
form_ref: &NodeRef,
|
||||
is_admin: bool,
|
||||
email_is_required: bool,
|
||||
is_admin: IsAdmin,
|
||||
email_is_required: EmailIsRequired,
|
||||
) -> Result<Vec<AttributeValue>> {
|
||||
let form = form_ref.cast::<HtmlFormElement>().unwrap();
|
||||
let form_data = FormData::new_with_form(&form)
|
||||
|
@ -40,7 +46,7 @@ pub fn read_all_form_attributes(
|
|||
let all_values = schema
|
||||
.into_iter()
|
||||
.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> {
|
||||
let val = form_data
|
||||
.get_all(attr.name.as_str())
|
||||
|
|
Loading…
Reference in a new issue