Skip to content

Commit 02b35d5

Browse files
chore: API Endpoint support for setting action type (RocketChat#39983)
Co-authored-by: Aleksander Nicacio da Silva <aleksander.silva@rocket.chat>
1 parent cc2eb02 commit 02b35d5

File tree

6 files changed

+102
-34
lines changed

6 files changed

+102
-34
lines changed

apps/meteor/app/api/server/v1/settings.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
TwitterOAuthConfiguration,
77
OAuthConfiguration,
88
} from '@rocket.chat/core-typings';
9-
import { isSettingAction, isSettingColor } from '@rocket.chat/core-typings';
9+
import { isActionSettingWithEndpoint, isSettingAction, isSettingColor } from '@rocket.chat/core-typings';
1010
import { LoginServiceConfiguration as LoginServiceConfigurationModel, Settings } from '@rocket.chat/models';
1111
import {
1212
ajv,
@@ -348,7 +348,12 @@ API.v1.post(
348348

349349
const { bodyParams } = this;
350350

351-
if (isSettingAction(setting) && isSettingsUpdatePropsActions(bodyParams) && bodyParams.execute) {
351+
if (
352+
isSettingAction(setting) &&
353+
isSettingsUpdatePropsActions(bodyParams) &&
354+
bodyParams.execute &&
355+
!isActionSettingWithEndpoint(setting.value)
356+
) {
352357
await Meteor.callAsync(setting.value);
353358
return API.v1.success();
354359
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Button, FieldRow, FieldHint } from '@rocket.chat/fuselage';
2+
import type { TranslationKey } from '@rocket.chat/ui-contexts';
3+
import { useToastMessageDispatch } from '@rocket.chat/ui-contexts';
4+
import type { ReactElement } from 'react';
5+
import { useTranslation } from 'react-i18next';
6+
7+
import type { SettingInputProps } from './types';
8+
9+
export type ActionInputBaseProps = SettingInputProps & {
10+
actionText: TranslationKey;
11+
sectionChanged: boolean;
12+
onAction: () => Promise<{ message: TranslationKey; params?: string[] }>;
13+
};
14+
15+
function ActionInputBase({ actionText, hint, disabled, sectionChanged, onAction }: ActionInputBaseProps): ReactElement {
16+
const { t } = useTranslation();
17+
const dispatchToastMessage = useToastMessageDispatch();
18+
19+
const handleClick = async (): Promise<void> => {
20+
try {
21+
const data = await onAction();
22+
const params = data.params || [];
23+
dispatchToastMessage({ type: 'success', message: t(data.message, { postProcess: 'sprintf', sprintf: params }) });
24+
} catch (error) {
25+
dispatchToastMessage({ type: 'error', message: error });
26+
}
27+
};
28+
29+
return (
30+
<>
31+
<FieldRow>
32+
<Button disabled={disabled || sectionChanged} primary onClick={handleClick}>
33+
{t(actionText)}
34+
</Button>
35+
</FieldRow>
36+
{sectionChanged && <FieldHint>{t('Save_to_enable_this_action')}</FieldHint>}
37+
{hint && <FieldHint>{hint}</FieldHint>}
38+
</>
39+
);
40+
}
41+
42+
export default ActionInputBase;
Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,25 @@
1+
import { isActionSettingWithEndpoint } from '@rocket.chat/core-typings';
12
import type { ServerMethods } from '@rocket.chat/ddp-client';
2-
import { Button, FieldRow, FieldHint } from '@rocket.chat/fuselage';
3+
import type { PathPattern, Method } from '@rocket.chat/rest-typings';
34
import type { TranslationKey } from '@rocket.chat/ui-contexts';
4-
import { useMethod, useToastMessageDispatch } from '@rocket.chat/ui-contexts';
55
import type { ReactElement } from 'react';
6-
import { useTranslation } from 'react-i18next';
76

7+
import EndpointActionInput from './EndpointActionInput';
8+
import MethodActionInput from './MethodActionInput';
89
import type { SettingInputProps } from './types';
910

1011
type ActionSettingInputProps = SettingInputProps & {
11-
value: keyof ServerMethods;
12+
value: keyof ServerMethods | { method: Method; path: PathPattern };
1213
actionText: TranslationKey;
1314
sectionChanged: boolean;
1415
};
1516

16-
function ActionSettingInput({ actionText, value, hint, disabled, sectionChanged }: ActionSettingInputProps): ReactElement {
17-
const { t } = useTranslation();
17+
function ActionSettingInput({ value, ...rest }: ActionSettingInputProps): ReactElement {
18+
if (isActionSettingWithEndpoint(value)) {
19+
return <EndpointActionInput endpoint={{ method: value.method, path: value.path }} {...rest} />;
20+
}
1821

19-
const dispatchToastMessage = useToastMessageDispatch();
20-
const actionMethod = useMethod(value);
21-
22-
const handleClick = async (): Promise<void> => {
23-
try {
24-
const data: { message: TranslationKey; params?: string[] } = await actionMethod();
25-
26-
const params = data.params || [];
27-
dispatchToastMessage({ type: 'success', message: t(data.message, { postProcess: 'sprintf', sprintf: params }) });
28-
} catch (error) {
29-
dispatchToastMessage({ type: 'error', message: error });
30-
}
31-
};
32-
33-
return (
34-
<>
35-
<FieldRow>
36-
<Button disabled={disabled || sectionChanged} primary onClick={handleClick}>
37-
{t(actionText)}
38-
</Button>
39-
</FieldRow>
40-
{sectionChanged && <FieldHint>{t('Save_to_enable_this_action')}</FieldHint>}
41-
{hint && <FieldHint>{hint}</FieldHint>}
42-
</>
43-
);
22+
return <MethodActionInput value={value} {...rest} />;
4423
}
4524

4625
export default ActionSettingInput;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { Method, PathPattern } from '@rocket.chat/rest-typings';
2+
import { useEndpoint } from '@rocket.chat/ui-contexts';
3+
4+
import type { ActionInputBaseProps } from './ActionInputBase';
5+
import ActionInputBase from './ActionInputBase';
6+
7+
type EndpointActionInputProps = Omit<ActionInputBaseProps, 'onAction'> & {
8+
endpoint: {
9+
method: Method;
10+
path: PathPattern;
11+
};
12+
};
13+
14+
function EndpointActionInput({ endpoint, ...rest }: EndpointActionInputProps) {
15+
const callEndpoint = useEndpoint(endpoint.method, endpoint.path);
16+
return <ActionInputBase onAction={() => callEndpoint({} as never)} {...rest} />;
17+
}
18+
19+
export default EndpointActionInput;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { ServerMethods } from '@rocket.chat/ddp-client';
2+
import { useMethod } from '@rocket.chat/ui-contexts';
3+
4+
import type { ActionInputBaseProps } from './ActionInputBase';
5+
import ActionInputBase from './ActionInputBase';
6+
7+
type MethodActionInputProps = Omit<ActionInputBaseProps, 'onAction'> & {
8+
value: keyof ServerMethods;
9+
};
10+
11+
function MethodActionInput({ value, ...rest }: MethodActionInputProps) {
12+
const actionMethod = useMethod(value);
13+
return <ActionInputBase onAction={actionMethod} {...rest} />;
14+
}
15+
16+
export default MethodActionInput;

packages/core-typings/src/ISetting.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ export enum SettingEditor {
88

99
export type SettingValueMultiSelect = (string | number)[];
1010
export type SettingValueRoomPick = { _id: string; name?: string }[];
11+
export type SettingValueAction = { method: string; path: string };
1112
export type SettingValue =
1213
| string
1314
| boolean
1415
| number
1516
| SettingValueMultiSelect
1617
| SettingValueRoomPick
18+
| SettingValueAction
1719
| Date
1820
| { url?: string; defaultUrl?: string }
1921
| undefined
@@ -122,9 +124,11 @@ interface ISettingCode extends ISettingBase {
122124
code?: string;
123125
}
124126

127+
type SettingActionEndpoint = { method: 'GET' | 'POST' | 'PUT' | 'DELETE'; path: string };
128+
125129
interface ISettingAction extends ISettingBase {
126130
type: 'action';
127-
value: string;
131+
value: string | SettingActionEndpoint;
128132
actionText?: string;
129133
}
130134

@@ -160,6 +164,9 @@ export const isSettingCode = (setting: ISettingBase): setting is ISettingCode =>
160164

161165
export const isSettingAction = (setting: ISettingBase): setting is ISettingAction => setting.type === 'action';
162166

167+
export const isActionSettingWithEndpoint = (value: ISettingAction['value']): value is SettingActionEndpoint =>
168+
typeof value === 'object' && value !== null && 'method' in value && 'path' in value;
169+
163170
export const isSettingRange = (setting: ISettingBase): setting is ISettingRange => setting.type === 'range';
164171

165172
export interface ISettingStatistics {

0 commit comments

Comments
 (0)