diff --git a/packages/bruno-app/src/components/RequestPane/Settings/ToggleSelector/index.js b/packages/bruno-app/src/components/RequestPane/Settings/ToggleSelector/index.js
index f0294aee9..899fc5cf7 100644
--- a/packages/bruno-app/src/components/RequestPane/Settings/ToggleSelector/index.js
+++ b/packages/bruno-app/src/components/RequestPane/Settings/ToggleSelector/index.js
@@ -57,7 +57,8 @@ const ToggleSelector = ({
`}
/>
-
+ {(label || description)
+ &&
@@ -66,7 +67,7 @@ const ToggleSelector = ({
{description}
)}
-
+
}
);
};
diff --git a/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js
index b7351d1ff..e5d182af3 100644
--- a/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js
+++ b/packages/bruno-app/src/components/RequestPane/WSRequestPane/index.js
@@ -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 ;
}
case 'settings': {
- return TBD
;
+ return ;
}
case 'auth': {
return ;
diff --git a/packages/bruno-app/src/components/RequestPane/WSSettingsPane/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/WSSettingsPane/StyledWrapper.js
new file mode 100644
index 000000000..2037cd9a0
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/WSSettingsPane/StyledWrapper.js
@@ -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;
diff --git a/packages/bruno-app/src/components/RequestPane/WSSettingsPane/index.js b/packages/bruno-app/src/components/RequestPane/WSSettingsPane/index.js
new file mode 100644
index 000000000..85348403d
--- /dev/null
+++ b/packages/bruno-app/src/components/RequestPane/WSSettingsPane/index.js
@@ -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}} 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 (
+
+
+
+
+
+
+
+ Timeout in milliseconds
+
+
+
+ }
+ />
+
+
+
+ onChangeConnectionTimeout(newValue)}
+ collection={collection}
+ />
+
+
+
+
+
+
+
+
+ Keep the websocket alive by sending ping requests to the server at every interval (in millseconds)
+
+
+
+ 0 (zero) = off
+
+
+ }
+ />
+
+
+
+ onChangeKeepAliveInterval(newValue)}
+ collection={collection}
+ />
+
+
+
+
+ {/* Variation 2 */}
+ {/*
+
+
+
+
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ onToggleKeepAlive();
+ }}
+ size="medium"
+ />
+
+
+
+
+
+
+ onChangeKeepAliveInterval(newValue)}
+ collection={collection}
+ />
+
+
+
+
+ */}
+
+ {/* Variation 3 */}
+ {/*
+
+
+ {
+ onToggleKeepAlive();
+ }}
+ size="medium"
+ />
+
+ {keepAlive ? (
+ <>
+
+
+
+
+
+ onChangeKeepAliveInterval(newValue)}
+ collection={collection}
+ />
+
+
+
+ >
+ ) : null}
+ */}
+
+ );
+};
+
+export default WSSettingsPane;
diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js
index 99008f286..bc88b11a3 100644
--- a/packages/bruno-lang/v2/src/bruToJson.js
+++ b/packages/bruno-lang/v2/src/bruToJson.js
@@ -279,6 +279,21 @@ const mapPairListToKeyValPair = (pairList = []) => {
return _.merge({}, ...pairList[0]);
};
+/**
+ * @param {Record} 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
}
};
},
diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js
index 3a24808da..b0adb2f4f 100644
--- a/packages/bruno-schema/src/collections/index.js
+++ b/packages/bruno-schema/src/collections/index.js
@@ -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.