Skip to content

Commit

Permalink
feat(console, phrases): update the supported webhook events
Browse files Browse the repository at this point in the history
update the supported webhook events
  • Loading branch information
simeng-li committed May 14, 2024
1 parent 6a3bc5e commit eb12246
Show file tree
Hide file tree
Showing 21 changed files with 387 additions and 31 deletions.
67 changes: 41 additions & 26 deletions packages/console/src/components/BasicWebhookForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import { type HookEvent, type Hook, type HookConfig, InteractionHookEvent } from '@logto/schemas';
import { type Hook, type HookConfig, type HookEvent } from '@logto/schemas';
import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import { hookEventLabel } from '@/consts/webhooks';
import { CheckboxGroup } from '@/ds-components/Checkbox';
import {
dataHookEventsLabel,
interactionHookEvents,
schemaGroupedDataHookEvents,
} from '@/consts/webhooks';
import CategorizedCheckboxGroup, {
type CheckboxOptionGroup,
} from '@/ds-components/Checkbox/CategorizedCheckboxGroup';
import FormField from '@/ds-components/FormField';
import TextInput from '@/ds-components/TextInput';
import { uriValidator } from '@/utils/validator';

import * as styles from './index.module.scss';

// TODO: Implement all hook events
const hookEventOptions = Object.values(InteractionHookEvent).map((event) => ({
title: hookEventLabel[event],
value: event,
}));
const hookEventGroups: Array<CheckboxOptionGroup<HookEvent>> = [
...schemaGroupedDataHookEvents.map(([schema, events]) => ({
title: dataHookEventsLabel[schema],
options: events.map((event) => ({
value: event,
})),
})),
{
title: 'webhooks.schemas.interaction',
options: interactionHookEvents.map((event) => ({
value: event,
})),
},
];

export type BasicWebhookFormType = {
name: Hook['name'];
Expand All @@ -32,24 +47,6 @@ function BasicWebhookForm() {

return (
<>
<FormField title="webhooks.create_form.events">
<div className={styles.formFieldDescription}>
{t('webhooks.create_form.events_description')}
</div>
<Controller
name="events"
control={control}
defaultValue={[]}
rules={{
validate: (value) =>
value.length === 0 ? t('webhooks.create_form.missing_event_error') : true,
}}
render={({ field: { onChange, value } }) => (
<CheckboxGroup options={hookEventOptions} value={value} onChange={onChange} />
)}
/>
{errors.events && <div className={styles.errorMessage}>{errors.events.message}</div>}
</FormField>
<FormField isRequired title="webhooks.create_form.name">
<TextInput
{...register('name', { required: true })}
Expand All @@ -71,6 +68,24 @@ function BasicWebhookForm() {
error={errors.url?.type === 'required' ? true : errors.url?.message}
/>
</FormField>
<FormField
title="webhooks.create_form.events"
tip={t('webhooks.create_form.events_description')}
>
<Controller
name="events"
control={control}
defaultValue={[]}
rules={{
validate: (value) =>
value.length === 0 ? t('webhooks.create_form.missing_event_error') : true,
}}
render={({ field: { onChange, value } }) => (
<CategorizedCheckboxGroup value={value} groups={hookEventGroups} onChange={onChange} />
)}
/>
{errors.events && <div className={styles.errorMessage}>{errors.events.message}</div>}
</FormField>
</>
);
}
Expand Down
54 changes: 53 additions & 1 deletion packages/console/src/consts/webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { type AdminConsoleKey } from '@logto/phrases';
import { InteractionHookEvent, type LogKey } from '@logto/schemas';
import {
DataHookSchema,
InteractionHookEvent,
hookEvents,
type DataHookEvent,
type LogKey,
} from '@logto/schemas';

type HookEventLabel = {
// TODO: Implement all hook events
Expand All @@ -12,6 +18,15 @@ export const hookEventLabel = Object.freeze({
[InteractionHookEvent.PostSignIn]: 'webhooks.events.post_sign_in',
}) satisfies HookEventLabel;

export const dataHookEventsLabel = Object.freeze({
[DataHookSchema.User]: 'webhooks.schemas.user',
[DataHookSchema.Organization]: 'webhooks.schemas.organization',
[DataHookSchema.Role]: 'webhooks.schemas.role',
[DataHookSchema.Scope]: 'webhooks.schemas.scope',
[DataHookSchema.OrganizationRole]: 'webhooks.schemas.organization_role',
[DataHookSchema.OrganizationScope]: 'webhooks.schemas.organization_scope',
} satisfies Record<DataHookSchema, AdminConsoleKey>);

type HookEventLogKey = {
// TODO: Implement all hook events
[key in InteractionHookEvent]: LogKey;
Expand All @@ -22,3 +37,40 @@ export const hookEventLogKey = Object.freeze({
[InteractionHookEvent.PostResetPassword]: 'TriggerHook.PostResetPassword',
[InteractionHookEvent.PostSignIn]: 'TriggerHook.PostSignIn',
}) satisfies HookEventLogKey;

const dataHookEvents: DataHookEvent[] = hookEvents.filter(
(event): event is DataHookEvent => !(event in InteractionHookEvent)
);

const isDataHookSchema = (schema: string): schema is DataHookSchema => schema in DataHookSchema;

// Group DataHook events by schema
const schemaGroupedDataHookEventsMap = dataHookEvents.reduce<Map<DataHookSchema, DataHookEvent[]>>(
(eventGroup, event) => {
const [schema] = event.split('.');

if (schema && isDataHookSchema(schema)) {
eventGroup.set(schema, [...(eventGroup.get(schema) ?? []), event]);
}

return eventGroup;
},
new Map()
);

export const interactionHookEvents = Object.values(InteractionHookEvent);

const hookEventSchemaOrder: {
[key in DataHookSchema]: number;
} = {
[DataHookSchema.User]: 0,
[DataHookSchema.Organization]: 1,
[DataHookSchema.Role]: 2,
[DataHookSchema.OrganizationRole]: 3,
[DataHookSchema.Scope]: 4,
[DataHookSchema.OrganizationScope]: 5,
};

export const schemaGroupedDataHookEvents = Array.from(schemaGroupedDataHookEventsMap.entries())
.slice()
.sort(([schemaA], [schemaB]) => hookEventSchemaOrder[schemaA] - hookEventSchemaOrder[schemaB]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@use '@/scss/underscore' as _;

.groupTitle {
font: var(--font-body-2);
color: var(--color-text-secondary);
margin-bottom: _.unit(2);
}

.groupList {
// max two column
gap: _.unit(5);
display: grid;
grid-template-columns: repeat(2, 1fr);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { type AdminConsoleKey } from '@logto/phrases';
import classNames from 'classnames';

import DynamicT from '@/ds-components/DynamicT';

import CheckboxGroup, { type Option } from '../CheckboxGroup';

import * as styles from './index.module.scss';

export type CheckboxOptionGroup<T> = {
title: AdminConsoleKey;
options: Array<Option<T>>;
};

type Props<T> = {
readonly groups: Array<CheckboxOptionGroup<T>>;
readonly value: T[];
readonly onChange: (value: T[]) => void;
readonly className?: string;
};

function CategorizedCheckboxGroup<T extends string>({
groups,
value: checkedValues,
onChange,
className,
}: Props<T>) {
return (
<div className={classNames(styles.groupList, className)}>
{groups.map(({ title, options }) => (
<div key={title}>
<div className={styles.groupTitle}>
<DynamicT forKey={title} />
</div>
<CheckboxGroup options={options} value={checkedValues} onChange={onChange} />
</div>
))}
</div>
);
}

export default CategorizedCheckboxGroup;
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import Checkbox from '../Checkbox';

import * as styles from './index.module.scss';

type Option<T> = {
title: AdminConsoleKey;
export type Option<T> = {
title?: AdminConsoleKey;
tag?: ReactNode;
value: T;
};
Expand Down Expand Up @@ -42,7 +42,7 @@ function CheckboxGroup<T extends string>({
key={value}
label={
<>
<DynamicT forKey={title} />
{title ? <DynamicT forKey={title} /> : value}
{tag}
</>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ const webhooks = {
post_sign_in: 'Anmelden',
post_reset_password: 'Passwort zurücksetzen',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: 'Name',
events: 'Ereignisse',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ const webhooks = {
post_sign_in: 'Sign in',
post_reset_password: 'Reset password',
},
schemas: {
interaction: 'User interaction',
user: 'User',
organization: 'Organization',
role: 'Role',
scope: 'Permission',
organization_role: 'Organization role',
organization_scope: 'Organization permission',
},
table: {
name: 'Name',
events: 'Events',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ const webhooks = {
post_sign_in: 'Iniciar sesión',
post_reset_password: 'Restablecer contraseña',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: 'Nombre',
events: 'Eventos',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ const webhooks = {
post_sign_in: 'Connectez-vous',
post_reset_password: 'Réinitialiser le mot de passe',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: 'Nom',
events: 'Événements',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ const webhooks = {
post_sign_in: 'Accedi',
post_reset_password: 'Reimposta password',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: 'Nome',
events: 'Eventi',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ const webhooks = {
post_sign_in: 'サインインする',
post_reset_password: 'パスワードをリセットする',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: '名前',
events: 'イベント',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ const webhooks = {
post_sign_in: '로그인',
post_reset_password: '비밀번호 재설정',
},
schemas: {
/** UNTRANSLATED */
interaction: 'User interaction',
/** UNTRANSLATED */
user: 'User',
/** UNTRANSLATED */
organization: 'Organization',
/** UNTRANSLATED */
role: 'Role',
/** UNTRANSLATED */
scope: 'Permission',
/** UNTRANSLATED */
organization_role: 'Organization role',
/** UNTRANSLATED */
organization_scope: 'Organization permission',
},
table: {
name: '이름',
events: '이벤트',
Expand Down
Loading

0 comments on commit eb12246

Please sign in to comment.