mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 05:05:39 +00:00
feat: ws settings
This commit is contained in:
@@ -57,7 +57,8 @@ const ToggleSelector = ({
|
||||
`}
|
||||
/>
|
||||
</button>
|
||||
<div className="flex flex-col">
|
||||
{(label || description)
|
||||
&& <div className="flex flex-col">
|
||||
<label className="text-xs font-medium text-gray-900 dark:text-gray-100">
|
||||
{label}
|
||||
</label>
|
||||
@@ -66,7 +67,7 @@ const ToggleSelector = ({
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getPropertyFromDraftOrRequest } from 'utils/collections/index';
|
||||
import WsBody from '../WsBody/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import WSAuth from './WSAuth';
|
||||
import WSSettingsPane from '../WSSettingsPane/index';
|
||||
|
||||
const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -43,7 +44,7 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
return <RequestHeaders item={item} collection={collection} addHeaderText="Add Metadata" />;
|
||||
}
|
||||
case 'settings': {
|
||||
return <div className="flex w-full mt-10 justify-center text-neutral-400">TBD</div>;
|
||||
return <WSSettingsPane item={item} collection={collection} />;
|
||||
}
|
||||
case 'auth': {
|
||||
return <WSAuth item={item} collection={collection} />;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
|
||||
.tooltip-mod {
|
||||
font-size: 11px !important;
|
||||
width: 150px !important;
|
||||
|
||||
& ul {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
& ul > li {
|
||||
list-style: circle;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,193 @@
|
||||
import React from 'react';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import get from 'lodash/get';
|
||||
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateItemSettings } from 'providers/ReduxStore/slices/collections';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
|
||||
import ToggleSelector from '../Settings/ToggleSelector/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import Accordion from 'components/Accordion';
|
||||
import InfoTip from 'components/InfoTip/index';
|
||||
|
||||
/**
|
||||
* @param {string} propertyKey
|
||||
* @param {{draft?:Record<string,unknown>}} item
|
||||
* @returns
|
||||
*/
|
||||
const getPropertyFromDraftOrRequest = (propertyKey, item) =>
|
||||
item.draft ? get(item, `draft.${propertyKey}`, {}) : get(item, propertyKey, {});
|
||||
|
||||
const WSSettingsPane = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme, theme } = useTheme();
|
||||
|
||||
const { keepAlive, connectionTimeout, keepAliveInterval } = getPropertyFromDraftOrRequest('settings', item);
|
||||
|
||||
const onToggleKeepAlive = () => {
|
||||
dispatch(
|
||||
updateItemSettings({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
settings: { keepAlive: !keepAlive }
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onChangeConnectionTimeout = (val) => {
|
||||
dispatch(
|
||||
updateItemSettings({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
settings: { connectionTimeout: val }
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const onChangeKeepAliveInterval = (val) => {
|
||||
dispatch(
|
||||
updateItemSettings({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
settings: { keepAliveInterval: val }
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="flex flex-col mt-4 gap-4 w-full">
|
||||
<section className="grid gap-4 items-center grid-cols-2">
|
||||
<div>
|
||||
<label className="font-medium mb-2">Connection Timeout</label>
|
||||
<InfoTip
|
||||
infotipId="setting-connection-timeout"
|
||||
className="tooltip-mod max-w-lg"
|
||||
content={
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
Timeout in milliseconds
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={connectionTimeout}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => onChangeConnectionTimeout(newValue)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="font-medium mb-2">Keep Alive Interval</label>
|
||||
<InfoTip
|
||||
infotipId="setting-keep-alive"
|
||||
className="tooltip-mod max-w-lg"
|
||||
content={
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
Keep the websocket alive by sending ping requests to the server at every interval (in millseconds)
|
||||
</span>
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
0 (zero) = off
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={keepAliveInterval}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => onChangeKeepAliveInterval(newValue)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Variation 2 */}
|
||||
{/* <Accordion>
|
||||
<Accordion.Item className="!border-0">
|
||||
<Accordion.Header
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0
|
||||
}}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<label className="font-medium">Keep Alive</label>
|
||||
<ToggleSelector
|
||||
checked={keepAlive}
|
||||
onChange={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onToggleKeepAlive();
|
||||
}}
|
||||
size="medium"
|
||||
/>
|
||||
</div>
|
||||
</Accordion.Header>
|
||||
<Accordion.Content>
|
||||
<div>
|
||||
<label className="flex font-medium mb-2">Keep Alive Interval (ms)</label>
|
||||
<div className="flex-1 w-1/2 single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={keepAliveInterval}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => onChangeKeepAliveInterval(newValue)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
</Accordion> */}
|
||||
|
||||
{/* Variation 3 */}
|
||||
{/* <section>
|
||||
<div className="flex justify-between">
|
||||
<label className="font-medium">Keep Alive</label>
|
||||
<ToggleSelector
|
||||
checked={keepAlive}
|
||||
onChange={(e) => {
|
||||
onToggleKeepAlive();
|
||||
}}
|
||||
size="medium"
|
||||
/>
|
||||
</div>
|
||||
{keepAlive ? (
|
||||
<>
|
||||
<div className="border-t border-neutral-200 my-2"></div>
|
||||
<div className="p-2">
|
||||
<div>
|
||||
<label className="flex font-medium mb-2">Keep Alive Interval (ms)</label>
|
||||
<div className="flex-1 w-1/2 single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={keepAliveInterval}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => onChangeKeepAliveInterval(newValue)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</section> */}
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default WSSettingsPane;
|
||||
@@ -279,6 +279,21 @@ const mapPairListToKeyValPair = (pairList = []) => {
|
||||
return _.merge({}, ...pairList[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Record<unknown,unknown>} obj
|
||||
* @returns {(key:string, opts?:{fallback: number })=>number|undefined}
|
||||
*/
|
||||
const createGetNumFromRecord =
|
||||
(obj) =>
|
||||
(key, { fallback } = {}) => {
|
||||
if (!(key in obj)) return fallback;
|
||||
const asNumber = typeof obj[key] === 'number' ? obj[key] : Number(obj.key);
|
||||
if (isNaN(asNumber)) {
|
||||
return fallback;
|
||||
}
|
||||
return asNumber;
|
||||
};
|
||||
|
||||
const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
BruFile(tags) {
|
||||
if (!tags || !tags.ast || !tags.ast.length) {
|
||||
@@ -410,9 +425,20 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
settings(_1, dictionary) {
|
||||
let settings = mapPairListToKeyValPair(dictionary.ast);
|
||||
|
||||
const getNumFromRecord = createGetNumFromRecord(settings);
|
||||
const keepAliveInterval = getNumFromRecord('keepAliveInterval', {
|
||||
fallback: 10000
|
||||
});
|
||||
const connectionTimeout = getNumFromRecord('connectionTimeout', {
|
||||
fallback: 250
|
||||
});
|
||||
|
||||
return {
|
||||
settings: {
|
||||
encodeUrl: typeof settings.encodeUrl === 'boolean' ? settings.encodeUrl : settings.encodeUrl === 'true'
|
||||
encodeUrl: typeof settings.encodeUrl === 'boolean' ? settings.encodeUrl : settings.encodeUrl === 'true',
|
||||
keepAlive: typeof settings.keepAlive === 'boolean' ? settings.keepAlive : settings.keepAlive === 'true',
|
||||
keepAliveInterval,
|
||||
connectionTimeout
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -417,6 +417,22 @@ const wsRequestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const wsSettingsSchema = Yup.object({
|
||||
settings: Yup.object({
|
||||
connectionTimeout: Yup.number()
|
||||
.default(1000)
|
||||
.min(250),
|
||||
keepAlive: Yup.boolean().default(false),
|
||||
keepAliveInterval: Yup.number()
|
||||
.default(10000)
|
||||
.min(500)
|
||||
// max 2 minutes
|
||||
.max(2 * 60 * 1000),
|
||||
}).noUnknown(true)
|
||||
.strict()
|
||||
.nullable()
|
||||
});
|
||||
|
||||
const folderRootSchema = Yup.object({
|
||||
request: Yup.object({
|
||||
headers: Yup.array().of(keyValueSchema).nullable(),
|
||||
@@ -472,12 +488,16 @@ const itemSchema = Yup.object({
|
||||
})
|
||||
})
|
||||
}),
|
||||
settings: Yup.object({
|
||||
encodeUrl: Yup.boolean().nullable()
|
||||
})
|
||||
.noUnknown(true)
|
||||
settings: Yup.mixed()
|
||||
.when('type', {
|
||||
is: (type) => type === 'ws-request',
|
||||
then: wsSettingsSchema,
|
||||
otherwise: Yup.object({
|
||||
encodeUrl: Yup.boolean().nullable()
|
||||
}).noUnknown(true)
|
||||
.strict()
|
||||
.nullable(),
|
||||
.nullable()
|
||||
}),
|
||||
fileContent: Yup.string().when('type', {
|
||||
// If the type is 'js', the fileContent field is expected to be a string.
|
||||
// This can include an empty string, indicating that the JS file may not have any content.
|
||||
|
||||
Reference in New Issue
Block a user