Store HTTP request json body as a string (#12931)
Variables can be used without surrounding quotes. The formatting is also preserved for raw json. We would have to do additional work if we want to add other types of bodies, like form data. ## Demo https://github.com/user-attachments/assets/498dd9c8-6654-4440-9ab0-35bad5e34ca8 Closes https://github.com/twentyhq/core-team-issues/issues/1129 Related to https://github.com/twentyhq/core-team-issues/issues/1117. Doesn't solve the issue for webhooks but does for http body input.
This commit is contained in:
committed by
GitHub
parent
8272e5dfd0
commit
d4fe8efd7f
@ -126,6 +126,7 @@ export const workflowHttpRequestActionSettingsSchema =
|
|||||||
z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])),
|
z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
.or(z.string())
|
||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,11 +7,15 @@ import {
|
|||||||
DEFAULT_JSON_BODY_PLACEHOLDER,
|
DEFAULT_JSON_BODY_PLACEHOLDER,
|
||||||
HttpRequestBody,
|
HttpRequestBody,
|
||||||
} from '@/workflow/workflow-steps/workflow-actions/http-request-action/constants/HttpRequest';
|
} from '@/workflow/workflow-steps/workflow-actions/http-request-action/constants/HttpRequest';
|
||||||
import { hasNonStringValues } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/hasNonStringValues';
|
import { parseHttpJsonBodyWithoutVariablesOrThrow } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/parseHttpJsonBodyWithoutVariablesOrThrow';
|
||||||
|
import { shouldDisplayRawJsonByDefault } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/shouldDisplayRawJsonByDefault';
|
||||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
import { isString } from '@sniptt/guards';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { parseJson } from 'twenty-shared/utils';
|
||||||
import { IconFileText, IconKey } from 'twenty-ui/display';
|
import { IconFileText, IconKey } from 'twenty-ui/display';
|
||||||
|
import { JsonValue } from 'type-fest';
|
||||||
import { KeyValuePairInput } from './KeyValuePairInput';
|
import { KeyValuePairInput } from './KeyValuePairInput';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -26,8 +30,8 @@ const StyledSelectDropdown = styled(Select)`
|
|||||||
|
|
||||||
type BodyInputProps = {
|
type BodyInputProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
defaultValue?: HttpRequestBody;
|
defaultValue?: HttpRequestBody | string;
|
||||||
onChange: (value?: HttpRequestBody) => void;
|
onChange: (value?: string) => void;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,11 +40,17 @@ export const BodyInput = ({
|
|||||||
onChange,
|
onChange,
|
||||||
readonly,
|
readonly,
|
||||||
}: BodyInputProps) => {
|
}: BodyInputProps) => {
|
||||||
const [isRawJson, setIsRawJson] = useState<boolean>(() =>
|
const defaultValueParsed = isString(defaultValue)
|
||||||
hasNonStringValues(defaultValue),
|
? (parseJson<JsonValue>(defaultValue) ?? {})
|
||||||
|
: defaultValue;
|
||||||
|
|
||||||
|
const [isRawJson, setIsRawJson] = useState(
|
||||||
|
shouldDisplayRawJsonByDefault(defaultValue),
|
||||||
);
|
);
|
||||||
const [jsonString, setJsonString] = useState<string | null>(
|
const [jsonString, setJsonString] = useState<string | null>(
|
||||||
JSON.stringify(defaultValue, null, 2),
|
isString(defaultValue)
|
||||||
|
? defaultValue
|
||||||
|
: JSON.stringify(defaultValue, null, 2),
|
||||||
);
|
);
|
||||||
const [errorMessage, setErrorMessage] = useState<string | undefined>();
|
const [errorMessage, setErrorMessage] = useState<string | undefined>();
|
||||||
|
|
||||||
@ -51,7 +61,8 @@ export const BodyInput = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JSON.parse(value);
|
parseHttpJsonBodyWithoutVariablesOrThrow(value);
|
||||||
|
|
||||||
setErrorMessage(undefined);
|
setErrorMessage(undefined);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -61,7 +72,7 @@ export const BodyInput = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyValueChange = (value: Record<string, string>) => {
|
const handleKeyValueChange = (value: Record<string, string>) => {
|
||||||
onChange(value);
|
onChange(JSON.stringify(value, null, 2));
|
||||||
setErrorMessage(undefined);
|
setErrorMessage(undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,8 +86,9 @@ export const BodyInput = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(value);
|
parseHttpJsonBodyWithoutVariablesOrThrow(value);
|
||||||
onChange(parsed);
|
|
||||||
|
onChange(value);
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing, validation will happen on blur
|
// Do nothing, validation will happen on blur
|
||||||
}
|
}
|
||||||
@ -121,7 +133,7 @@ export const BodyInput = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<KeyValuePairInput
|
<KeyValuePairInput
|
||||||
defaultValue={defaultValue as Record<string, string>}
|
defaultValue={defaultValueParsed as Record<string, string>}
|
||||||
onChange={handleKeyValueChange}
|
onChange={handleKeyValueChange}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
keyPlaceholder="Property name"
|
keyPlaceholder="Property name"
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export type KeyValuePair = {
|
|||||||
|
|
||||||
export type KeyValuePairInputProps = {
|
export type KeyValuePairInputProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
defaultValue?: Record<string, string>;
|
defaultValue?: Record<string, string> | Array<string>;
|
||||||
onChange: (value: Record<string, string>) => void;
|
onChange: (value: Record<string, string>) => void;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
keyPlaceholder?: string;
|
keyPlaceholder?: string;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { WorkflowHttpRequestAction } from '@/workflow/types/Workflow';
|
import { WorkflowHttpRequestAction } from '@/workflow/types/Workflow';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { expect, fn, within } from '@storybook/test';
|
import { expect, fn, waitFor, within } from '@storybook/test';
|
||||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||||
@ -8,7 +8,10 @@ import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/Workflow
|
|||||||
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
|
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
|
||||||
import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator';
|
import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator';
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow';
|
import {
|
||||||
|
getWorkflowNodeIdMock,
|
||||||
|
MOCKED_STEP_ID,
|
||||||
|
} from '~/testing/mock-data/workflow';
|
||||||
import { WorkflowEditActionHttpRequest } from '../WorkflowEditActionHttpRequest';
|
import { WorkflowEditActionHttpRequest } from '../WorkflowEditActionHttpRequest';
|
||||||
|
|
||||||
const DEFAULT_ACTION: WorkflowHttpRequestAction = {
|
const DEFAULT_ACTION: WorkflowHttpRequestAction = {
|
||||||
@ -149,3 +152,208 @@ export const ReadOnly: Story = {
|
|||||||
expect(methodSelect).toBeVisible();
|
expect(methodSelect).toBeVisible();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WithArrayStringBody: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'API Call with Array',
|
||||||
|
type: 'HTTP_REQUEST',
|
||||||
|
valid: true,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
url: 'https://api.example.com/tags',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: `[
|
||||||
|
"frontend",
|
||||||
|
"backend",
|
||||||
|
"database"
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies WorkflowHttpRequestAction,
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
expect(await canvas.findByText('Key/Value')).toBeVisible();
|
||||||
|
|
||||||
|
expect(await canvas.findByText('0')).toBeVisible();
|
||||||
|
expect(await canvas.findByText('1')).toBeVisible();
|
||||||
|
expect(await canvas.findByText('2')).toBeVisible();
|
||||||
|
|
||||||
|
expect(await canvas.findByText('frontend')).toBeVisible();
|
||||||
|
expect(await canvas.findByText('backend')).toBeVisible();
|
||||||
|
expect(await canvas.findByText('database')).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithObjectStringBody: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'API Call with Array',
|
||||||
|
type: 'HTTP_REQUEST',
|
||||||
|
valid: true,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
url: 'https://api.example.com/tags',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: `{
|
||||||
|
"hey": "frontend",
|
||||||
|
"oh": "backend",
|
||||||
|
"amazing": "database {{${MOCKED_STEP_ID}.salary}}"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies WorkflowHttpRequestAction,
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
expect(await canvas.findByText('Key/Value')).toBeVisible();
|
||||||
|
|
||||||
|
const textboxes = await waitFor(() => {
|
||||||
|
const elements = canvas.getAllByRole('textbox');
|
||||||
|
expect(elements.length).toBe(14);
|
||||||
|
return elements;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(textboxes[5]).toHaveTextContent('hey');
|
||||||
|
expect(textboxes[7]).toHaveTextContent('oh');
|
||||||
|
expect(textboxes[9]).toHaveTextContent('amazing');
|
||||||
|
|
||||||
|
expect(textboxes[6]).toHaveTextContent('frontend');
|
||||||
|
expect(textboxes[8]).toHaveTextContent('backend');
|
||||||
|
expect(textboxes[10]).toHaveTextContent('database Salary');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithArrayContainingNonStringVariablesBody: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'API Call with Array',
|
||||||
|
type: 'HTTP_REQUEST',
|
||||||
|
valid: true,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
url: 'https://api.example.com/tags',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: `[
|
||||||
|
"frontend",
|
||||||
|
{{${MOCKED_STEP_ID}.salary}},
|
||||||
|
"database"
|
||||||
|
]`,
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies WorkflowHttpRequestAction,
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
expect(await canvas.findByText('Raw JSON')).toBeVisible();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const textboxes = canvas.getAllByRole('textbox');
|
||||||
|
|
||||||
|
expect(textboxes[5]).toHaveTextContent(
|
||||||
|
'[ "frontend", Salary, "database"]',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithObjectContainingNonStringVariablesBody: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'API Call with Array',
|
||||||
|
type: 'HTTP_REQUEST',
|
||||||
|
valid: true,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
url: 'https://api.example.com/tags',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: `{
|
||||||
|
"speciality": "frontend",
|
||||||
|
"salary": {{${MOCKED_STEP_ID}.salary}}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies WorkflowHttpRequestAction,
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
expect(await canvas.findByText('Raw JSON')).toBeVisible();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const textboxes = canvas.getAllByRole('textbox');
|
||||||
|
|
||||||
|
expect(textboxes[5]).toHaveTextContent(
|
||||||
|
'{ "speciality": "frontend", "salary": Salary}',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export type HttpRequestFormData = {
|
|||||||
url: string;
|
url: string;
|
||||||
method: HttpMethod;
|
method: HttpMethod;
|
||||||
headers: Record<string, string>;
|
headers: Record<string, string>;
|
||||||
body?: HttpRequestBody;
|
body?: HttpRequestBody | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_JSON_BODY_PLACEHOLDER =
|
export const DEFAULT_JSON_BODY_PLACEHOLDER =
|
||||||
|
|||||||
@ -2,21 +2,17 @@ import { HttpRequestBody } from '../../constants/HttpRequest';
|
|||||||
import { hasNonStringValues } from '../hasNonStringValues';
|
import { hasNonStringValues } from '../hasNonStringValues';
|
||||||
|
|
||||||
describe('hasNonStringValues', () => {
|
describe('hasNonStringValues', () => {
|
||||||
it('should return false for undefined input', () => {
|
it('should return true for empty object', () => {
|
||||||
expect(hasNonStringValues(undefined)).toBe(false);
|
expect(hasNonStringValues({})).toBe(true);
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false for empty object', () => {
|
|
||||||
expect(hasNonStringValues({})).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false for object with only string values', () => {
|
it('should return false for object with only string values', () => {
|
||||||
expect(hasNonStringValues({ key1: 'value1', key2: 'value2' })).toBe(false);
|
expect(hasNonStringValues({ key1: 'value1', key2: 'value2' })).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false for object with null values', () => {
|
it('should return true for object with null values', () => {
|
||||||
const body: HttpRequestBody = { key1: null, key2: 'value' };
|
const body: HttpRequestBody = { key1: null, key2: 'value' };
|
||||||
expect(hasNonStringValues(body)).toBe(false);
|
expect(hasNonStringValues(body)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true for object with number values', () => {
|
it('should return true for object with number values', () => {
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import { HttpRequestBody } from '../constants/HttpRequest';
|
import { isString } from '@sniptt/guards';
|
||||||
|
import { JsonArray, JsonObject } from 'type-fest';
|
||||||
|
|
||||||
export const hasNonStringValues = (obj?: HttpRequestBody): boolean => {
|
export const hasNonStringValues = (obj: JsonObject | JsonArray): boolean => {
|
||||||
if (!obj) {
|
const values = Object.values(obj);
|
||||||
return false;
|
|
||||||
|
if (values.length === 0) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return Object.values(obj).some(
|
|
||||||
(value) =>
|
return !values.every((value) => isString(value));
|
||||||
value !== null && value !== undefined && typeof value !== 'string',
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { removeVariablesFromJson } from '@/workflow/workflow-variables/utils/removeVariablesFromJson';
|
||||||
|
|
||||||
|
export const parseHttpJsonBodyWithoutVariablesOrThrow = (value: string) => {
|
||||||
|
return JSON.parse(removeVariablesFromJson(value));
|
||||||
|
};
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { HttpRequestBody } from '@/workflow/workflow-steps/workflow-actions/http-request-action/constants/HttpRequest';
|
||||||
|
import { hasNonStringValues } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/hasNonStringValues';
|
||||||
|
import { removeVariablesFromJson } from '@/workflow/workflow-variables/utils/removeVariablesFromJson';
|
||||||
|
import { isObject, isString, isUndefined } from '@sniptt/guards';
|
||||||
|
import { parseJson } from 'twenty-shared/utils';
|
||||||
|
import { JsonValue } from 'type-fest';
|
||||||
|
|
||||||
|
export const shouldDisplayRawJsonByDefault = (
|
||||||
|
defaultValue: string | HttpRequestBody | undefined,
|
||||||
|
) => {
|
||||||
|
const defaultValueParsedWithoutVariables: JsonValue | undefined = isString(
|
||||||
|
defaultValue,
|
||||||
|
)
|
||||||
|
? (parseJson<JsonValue>(removeVariablesFromJson(defaultValue)) ?? {})
|
||||||
|
: defaultValue;
|
||||||
|
|
||||||
|
if (isUndefined(defaultValueParsedWithoutVariables)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
((isObject(defaultValueParsedWithoutVariables) ||
|
||||||
|
Array.isArray(defaultValueParsedWithoutVariables)) &&
|
||||||
|
hasNonStringValues(defaultValueParsedWithoutVariables)) ||
|
||||||
|
!(
|
||||||
|
isObject(defaultValueParsedWithoutVariables) ||
|
||||||
|
Array.isArray(defaultValueParsedWithoutVariables)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { CAPTURE_ALL_VARIABLE_TAG_INNER_REGEX } from '@/workflow/workflow-variables/constants/CaptureAllVariableTagInnerRegex';
|
||||||
|
|
||||||
|
export const removeVariablesFromJson = (json: string): string => {
|
||||||
|
return json.replaceAll(CAPTURE_ALL_VARIABLE_TAG_INNER_REGEX, 'null');
|
||||||
|
};
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { isString } from '@sniptt/guards';
|
||||||
import axios, { AxiosRequestConfig } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
import { WorkflowExecutor } from 'src/modules/workflow/workflow-executor/interfaces/workflow-executor.interface';
|
||||||
@ -52,7 +53,9 @@ export class HttpRequestWorkflowAction implements WorkflowExecutor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (['POST', 'PUT', 'PATCH'].includes(method) && body) {
|
if (['POST', 'PUT', 'PATCH'].includes(method) && body) {
|
||||||
axiosConfig.data = body;
|
const parsedBody = isString(body) ? JSON.parse(body) : body;
|
||||||
|
|
||||||
|
axiosConfig.data = parsedBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await axios(axiosConfig);
|
const response = await axios(axiosConfig);
|
||||||
|
|||||||
@ -2,13 +2,15 @@ export type WorkflowHttpRequestActionInput = {
|
|||||||
url: string;
|
url: string;
|
||||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
body?: Record<
|
body?:
|
||||||
string,
|
| Record<
|
||||||
| string
|
string,
|
||||||
| number
|
| string
|
||||||
| boolean
|
| number
|
||||||
| null
|
| boolean
|
||||||
| undefined
|
| null
|
||||||
| Array<string | number | boolean | null>
|
| undefined
|
||||||
>;
|
| Array<string | number | boolean | null>
|
||||||
|
>
|
||||||
|
| string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user