Skip to content

Commit

Permalink
Allow metadata editing (#137)
Browse files Browse the repository at this point in the history
* Allow editing metadata

* Hide edit sections if not defined

* Fix MultilineEdit misbehaving when deleting an item

* Add admin metadata test, fix public metadata test
  • Loading branch information
toonalbers authored Jan 5, 2024
1 parent 775bc3c commit c64e86f
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 114 deletions.
9 changes: 5 additions & 4 deletions kratos-admin-ui/src/components/multiline/multiline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function MultilineEdit(props: MultilineEditProps) {
<>
{data.map((value, index) => {
return (
<div key={index} style={{
<div key={[index, value].join('-')} style={{
display: "flex",
alignItems: "center",
paddingBottom: 5
Expand Down Expand Up @@ -72,10 +72,11 @@ export function MultilineEdit(props: MultilineEditProps) {
size="small"
icon={<Delete12Filled></Delete12Filled>}
onClick={(event => {
setData(data.filter((arrayvalue, arrayIndex) => {
const filtered = data.filter((arrayvalue, arrayIndex) => {
return arrayIndex !== index
}))
props.dataChanged(data)
})
setData(filtered)
props.dataChanged(filtered)
})}
></Button>
</Tooltip>
Expand Down
142 changes: 117 additions & 25 deletions kratos-admin-ui/src/components/traits/edit-traits.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, Checkbox, Field, Select, Text } from "@fluentui/react-components"
import { Identity, IdentityApi, IdentityState } from "@ory/kratos-client"
import { useEffect, useState } from "react"
import { SchemaField, SchemaService } from "../../service/schema-service";
import { FieldKind, SchemaField, SchemaService } from "../../service/schema-service";
import { getKratosConfig } from "../../config";
import { useHistory } from "react-router-dom";
import { RenderTraitField } from "./render-field";
Expand All @@ -28,7 +28,18 @@ export interface ValueObject {
state: IdentityState
traits: IdentityTraits;
publicMetadata: MetaData;
adminMedataData: MetaData;
adminMetadata: MetaData;
}

export function mapFieldKindToValueKey(fieldKind: FieldKind) {
switch (fieldKind) {
case "trait":
return "traits";
case "metadata_public":
return "publicMetadata";
case "metadata_admin":
return "adminMetadata"
}
}

function getButtonName(modi: Modi) {
Expand All @@ -43,10 +54,10 @@ async function fillTraits(identity: Identity, schemaFields: SchemaField[]): Prom
const map = await SchemaService.getTableDetailListModelFromKratosIdentity(identity);
const traits: IdentityTraits = {}

for (const [key, value] of Object.entries(map)) {
for (const [key, value] of Object.entries(map.traits)) {
if (key !== "key") {
schemaFields.forEach(f => {
if (f.name === key) {
if (f.name === key && f.fieldKind === "trait") {
if (f.parentName) {
if (!traits[f.parentName]) {
traits[f.parentName] = {}
Expand All @@ -62,7 +73,28 @@ async function fillTraits(identity: Identity, schemaFields: SchemaField[]): Prom
return traits;
}

async function fillMetadata(identity: Identity, schemaFields: SchemaField[], metadataKind: "metadata_public" | "metadata_admin"): Promise<IdentityTraits> {
const map = await SchemaService.getTableDetailListModelFromKratosIdentity(identity);
const metadata: MetaData = {}

for (const [key, value] of Object.entries(map[metadataKind])) {
if (key !== "key") {
schemaFields.forEach(f => {
if (f.name === key && f.fieldKind === metadataKind) {
if (f.parentName) {
if (!metadata[f.parentName]) {
metadata[f.parentName] = {}
}
metadata[f.parentName][f.name] = value
} else {
metadata[f.name] = value
}
}
});
}
}
return metadata;
}

async function performAction(modi: Modi, values: ValueObject, identity?: Identity, schemaId?: string): Promise<import("axios").AxiosResponse<Identity>> {
const kratosConfig = await getKratosConfig();
Expand All @@ -76,8 +108,8 @@ async function performAction(modi: Modi, values: ValueObject, identity?: Identit
schema_id: identity?.schema_id!,
traits: values.traits,
state: values.state,
metadata_public: identity?.metadata_public,
metadata_admin: identity?.metadata_admin
metadata_public: values.publicMetadata,
metadata_admin: values.adminMetadata
}
})
MessageService.Instance.dispatchMessage({
Expand All @@ -94,8 +126,8 @@ async function performAction(modi: Modi, values: ValueObject, identity?: Identit
createIdentityBody: {
schema_id: schemaId!,
traits: values.traits,
metadata_admin: kratosConfig.adminConfig.basePath,
metadata_public: kratosConfig.publicConfig.basePath
metadata_public: values.publicMetadata,
metadata_admin: values.adminMetadata
}
})
MessageService.Instance.dispatchMessage({
Expand Down Expand Up @@ -128,15 +160,15 @@ export function EditTraits(props: EditTraitsProps) {
valueObject = {
state: identity.state!,
traits: await fillTraits(identity, newSchemaFields),
publicMetadata: identity.metadata_public,
adminMedataData: identity.metadata_admin
publicMetadata: await fillMetadata(identity, newSchemaFields, "metadata_public"),
adminMetadata: await fillMetadata(identity, newSchemaFields, "metadata_admin")
}
} else if (props.schema && props.modi === "new") {
newSchemaFields = SchemaService.getSchemaFields(props.schema)
valueObject = {
state: "active",
traits: {},
adminMedataData: {},
adminMetadata: {},
publicMetadata: {}
}
} else {
Expand All @@ -157,6 +189,10 @@ export function EditTraits(props: EditTraitsProps) {
prepare()
})

const traits = schemaFields && schemaFields.filter((elem, _key) => elem.fieldKind === "trait")
const publicMetadata = schemaFields && schemaFields.filter((elem, _key) => elem.fieldKind === "metadata_public")
const adminMetadata = schemaFields && schemaFields.filter((elem, _key) => elem.fieldKind === "metadata_admin")

return (
<div>
<Text
Expand Down Expand Up @@ -190,7 +226,6 @@ export function EditTraits(props: EditTraitsProps) {
></Checkbox>
}


<Text
as="h2"
style={{
Expand All @@ -199,19 +234,76 @@ export function EditTraits(props: EditTraitsProps) {
marginTop: 10
}}
>Custom Traits</Text>
{schemaFields && values && schemaFields.map((elem, key) => {
return (
<div key={key}>
<RenderTraitField
schemaField={elem}
fieldValues={values}
setValues={(values) => {
setValues(values)
}}
></RenderTraitField>
</div>
)
})}
{ values && traits?.length ? (
traits.map((elem, key) => {
return (
<div key={key}>
<RenderTraitField
schemaField={elem}
fieldValues={values}
setValues={(values) => {
setValues(values)
}}
></RenderTraitField>
</div>
)
})
) : (
<p>None</p>
)}

{ publicMetadata?.length ? (
<div>
<Text
as="h2"
style={{
display: "block",
fontSize: 20,
marginTop: 10
}}
>Public Metadata</Text>
{values && publicMetadata.map((elem, key) => {
return (
<div key={key}>
<RenderTraitField
schemaField={elem}
fieldValues={values}
setValues={(values) => {
setValues(values)
}}
></RenderTraitField>
</div>
)
}) }
</div>
) : ( null ) }

{ adminMetadata?.length ? (
<div>
<Text
as="h2"
style={{
display: "block",
fontSize: 20,
marginTop: 10
}}
>Admin Metadata</Text>
{values && adminMetadata.map((elem, key) => {
return (
<div key={key}>
<RenderTraitField
schemaField={elem}
fieldValues={values}
setValues={(values) => {
setValues(values)
}}
></RenderTraitField>
</div>
)
})}
</div>
) : ( null ) }

{!errorText || <div className="alert alert-danger" style={{ marginTop: 15 }}>{errorText}</div>}
<div style={{ marginTop: 20 }}>
<div style={{ display: "flex", gap: 20, marginBottom: 15 }}>
Expand Down
56 changes: 0 additions & 56 deletions kratos-admin-ui/src/components/traits/metadata-renderer.tsx

This file was deleted.

27 changes: 17 additions & 10 deletions kratos-admin-ui/src/components/traits/render-field.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Label } from "@fluentui/react-components"
import { SchemaField, mapSchemaDataType } from "../../service/schema-service"
import { ValueObject } from "./edit-traits"
import { ValueObject, mapFieldKindToValueKey } from "./edit-traits"
import { SingleField } from "./single-field"
import { MultilineEdit } from "../multiline/multiline"

Expand All @@ -11,17 +11,23 @@ interface RenderTraitFieldProps {
}

function getDefaultValue(schemaField: SchemaField, values: ValueObject): any[] {
const fieldKindKey = mapFieldKindToValueKey(schemaField.fieldKind);
if (schemaField.type === "array") {
let value = [];
if (schemaField.parentName) {
if (values.traits[schemaField.parentName] && values.traits[schemaField.parentName][schemaField.name]) {
return values.traits[schemaField.parentName][schemaField.name];
if (values[fieldKindKey][schemaField.parentName] && values[fieldKindKey][schemaField.parentName][schemaField.name]) {
value = values[fieldKindKey][schemaField.parentName][schemaField.name];
}
} else {
if (values.traits[schemaField.name]) {
return values.traits[schemaField.name];
if (values[fieldKindKey][schemaField.name]) {
value = values[fieldKindKey][schemaField.name];
}
}
return []

if (!(value instanceof Array)) {
return [value];
}
return value;
}
throw new Error("Should not be called as non array object!")
}
Expand All @@ -45,13 +51,14 @@ export function RenderTraitField(props: RenderTraitFieldProps) {
<MultilineEdit
defaultData={getDefaultValue(props.schemaField, props.fieldValues)}
dataChanged={(data) => {
const fieldKindKey = mapFieldKindToValueKey(props.schemaField.fieldKind);
if (props.schemaField.parentName) {
if (!props.fieldValues.traits[props.schemaField.parentName]) {
props.fieldValues.traits[props.schemaField.parentName] = {}
if (!props.fieldValues[fieldKindKey][props.schemaField.parentName]) {
props.fieldValues[fieldKindKey][props.schemaField.parentName] = {}
}
props.fieldValues.traits[props.schemaField.parentName][props.schemaField.name] = data
props.fieldValues[fieldKindKey][props.schemaField.parentName][props.schemaField.name] = data
} else {
props.fieldValues.traits[props.schemaField.name] = data
props.fieldValues[fieldKindKey][props.schemaField.name] = data
}
props.setValues(props.fieldValues)
}}
Expand Down
Loading

0 comments on commit c64e86f

Please sign in to comment.