Skip to content

Commit d3b07b0

Browse files
authored
Merge pull request #137 from FlowTestAI/header-variables
feat: allow env variables to be used in request headers
2 parents 1f0011e + 8382b90 commit d3b07b0

5 files changed

Lines changed: 89 additions & 53 deletions

File tree

packages/flowtest-cli/graph/compute/requestnode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class requestNode extends Node {
119119

120120
if (this.nodeData.headers && this.nodeData.headers.length > 0) {
121121
this.nodeData.headers.map((pair, index) => {
122-
headers[pair.name] = pair.value;
122+
headers[computeVariables(pair.name, variablesDict)] = computeVariables(pair.value, variablesDict);
123123
});
124124
}
125125

src/components/atoms/common/TextEditor.js

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ const hideScrollbar = EditorView.theme({
4545
'.cm-content': {
4646
padding: '0', // Adjust padding to fit your needs
4747
overflow: 'auto',
48+
lineHeight: '1.75rem',
49+
fontSize: '1.125rem',
4850
},
4951
'.cm-scroller::-webkit-scrollbar': {
5052
display: 'none' /* For Chrome, Safari, and Opera */,
@@ -54,10 +56,12 @@ const hideScrollbar = EditorView.theme({
5456
},
5557
'&': {
5658
height: 'auto', // Adjust height to auto for single-line input
59+
cursor: 'text',
5760
},
5861
'.cm-placeholder': {
5962
color: '#aaa', // Placeholder text color
6063
},
64+
'&.cm-focused': { outline: 'none' },
6165
});
6266

6367
// Rebind the Enter key to do nothing
@@ -98,27 +102,13 @@ const highlightStyle = EditorView.baseTheme({
98102
});
99103

100104
export const TextEditor = ({ placeHolder, onChangeHandler, value, disableState, completionOptions, styles }) => {
101-
const editor1 = useRef();
102-
const [view, setView] = useState(null);
105+
const editorRef = useRef(null);
106+
const viewRef = useRef(null);
103107
const [dynamicOptions, setDynamicOptions] = useState(completionOptions);
104108

105-
if (view) {
106-
if (!isEqual(dynamicOptions, completionOptions)) {
107-
updateAutocompleteOptions(view, completionOptions);
108-
setDynamicOptions(completionOptions);
109-
}
110-
if (value != view.state.doc.toString()) {
111-
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: value } });
112-
}
113-
}
114-
115-
const onUpdate = EditorView.updateListener.of((v) => {
116-
if (onChangeHandler) {
117-
onChangeHandler(v.state.doc.toString());
118-
}
119-
});
120-
121109
useEffect(() => {
110+
if (!editorRef.current) return;
111+
122112
const state = EditorState.create({
123113
doc: value,
124114
extensions: [
@@ -127,7 +117,11 @@ export const TextEditor = ({ placeHolder, onChangeHandler, value, disableState,
127117
//myAutocomplete,
128118
//basicSetup,
129119
keymap.of([defaultKeymap, indentWithTab]),
130-
onUpdate,
120+
EditorView.updateListener.of((v) => {
121+
if (onChangeHandler && v.docChanged) {
122+
onChangeHandler(v.state.doc.toString());
123+
}
124+
}),
131125
//EditorState.readOnly.of(props.readOnly || false),
132126
history(),
133127
hideScrollbar,
@@ -138,18 +132,36 @@ export const TextEditor = ({ placeHolder, onChangeHandler, value, disableState,
138132
],
139133
});
140134

141-
const view = new EditorView({ state, parent: editor1.current });
142-
setView(view);
135+
const view = new EditorView({ state, parent: editorRef.current });
136+
viewRef.current = view;
143137

144138
return () => {
145139
view.destroy();
146-
setView(null);
147140
};
148141
}, []);
149142

150-
const mainStyles =
151-
'nodrag nowheel block rounded border border-slate-700 bg-background-light p-2.5 text-sm outline-none';
143+
useEffect(() => {
144+
const view = viewRef.current;
145+
if (!view) return;
146+
147+
// Update completion options if they've changed
148+
if (!isEqual(dynamicOptions, completionOptions)) {
149+
updateAutocompleteOptions(view, completionOptions);
150+
setDynamicOptions(completionOptions);
151+
}
152+
153+
// Update content if it's different from current editor content
154+
const currentContent = view.state.doc.toString();
155+
if (value !== currentContent) {
156+
view.dispatch({
157+
changes: { from: 0, to: currentContent.length, insert: value },
158+
});
159+
}
160+
}, [value, completionOptions]);
161+
162+
//const mainStyles =
163+
// 'nodrag nowheel block border border-slate-700 bg-background-light p-2.5 text-base outline-none';
152164
const intentStyles = disableState ? 'cursor-not-allowed text-slate-400' : 'text-slate-900';
153165

154-
return <div ref={editor1} className={`${mainStyles} ${intentStyles} ${styles}`}></div>;
166+
return <div ref={editorRef} className={`${styles}`}></div>;
155167
};

src/components/molecules/flow/graph/compute/requestnode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class requestNode extends Node {
9999

100100
if (this.nodeData.headers && this.nodeData.headers.length > 0) {
101101
this.nodeData.headers.map((pair, index) => {
102-
headers[pair.name] = pair.value;
102+
headers[computeVariables(pair.name, variablesDict)] = computeVariables(pair.value, variablesDict);
103103
});
104104
}
105105

src/components/molecules/flow/nodes/AuthNode.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import { useTabStore } from 'stores/TabStore';
1111
import { cloneDeep } from 'lodash';
1212

1313
const AuthNode = ({ id, data }) => {
14+
const validAuthValues = ['no-auth', 'basic-auth', 'bearer-token'];
1415
const setAuthNodeType = useCanvasStore((state) => state.setAuthNodeType);
1516
const setBasicAuthValues = useCanvasStore((state) => state.setBasicAuthValues);
1617
const setBearerTokenValue = useCanvasStore((state) => state.setBearerTokenValue);
17-
const [selected, setSelected] = useState(data.type ? data.type : 'no-auth');
18+
const [selected, setSelected] = useState(data.type && validAuthValues.includes(data.type) ? data.type : 'no-auth');
1819

1920
const handleBasicAuthValueChange = (value, option) => {
2021
setBasicAuthValues(id, option, value);
@@ -148,27 +149,33 @@ const AuthNode = ({ id, data }) => {
148149
name={'username'}
149150
value={data.username ? data.username : ''}
150151
completionOptions={getActiveVariables()}
151-
styles={'w-full'}
152+
styles={
153+
'w-full nodrag nowheel block rounded border border-slate-700 bg-background-light p-2.5 text-base outline-none'
154+
}
152155
/>
153156
<TextEditor
154157
placeHolder={`Password`}
155158
onChangeHandler={(value) => handleBasicAuthValueChange(value, 'password')}
156159
name={'password'}
157160
value={data.password ? data.password : ''}
158161
completionOptions={getActiveVariables()}
159-
styles={'w-full'}
162+
styles={
163+
'w-full nodrag nowheel block rounded border border-slate-700 bg-background-light p-2.5 text-base outline-none'
164+
}
160165
/>
161166
</div>
162167
)}
163168
{data.type === 'bearer-token' && (
164169
<div className='flex flex-col gap-2 py-4'>
165170
<TextEditor
166-
placeHolder={`Token`}
171+
placeHolder={'Token'}
167172
onChangeHandler={(value) => handleBearerTokenChange(value)}
168173
name={'token'}
169174
value={data.token ? data.token : ''}
170175
completionOptions={getActiveVariables()}
171-
styles={'w-full'}
176+
styles={
177+
'w-full nodrag nowheel block rounded border border-slate-700 bg-background-light p-2.5 text-base outline-none'
178+
}
172179
/>
173180
</div>
174181
)}

src/components/molecules/flow/nodes/RequestNode.js

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,28 @@ const RequestNode = ({ id, data }) => {
158158
);
159159
};
160160

161+
const headerNameChange = (index, updateName) => {
162+
const currentHeaders = useCanvasStore.getState().nodes.find((n) => n.id === id)?.data?.headers || [];
163+
const updatedHeaders = currentHeaders.map((p, idx) => {
164+
if (idx === index) {
165+
return { name: updateName, value: p.value };
166+
}
167+
return p;
168+
});
169+
setRequestNodeHeaders(id, updatedHeaders);
170+
};
171+
172+
const headerValueChange = (index, updateValue) => {
173+
const currentHeaders = useCanvasStore.getState().nodes.find((n) => n.id === id)?.data?.headers || [];
174+
const updatedHeaders = currentHeaders.map((p, idx) => {
175+
if (idx === index) {
176+
return { name: p.name, value: updateValue };
177+
}
178+
return p;
179+
});
180+
setRequestNodeHeaders(id, updatedHeaders);
181+
};
182+
161183
const renderHeaders = () => {
162184
return (
163185
<div>
@@ -173,34 +195,27 @@ const RequestNode = ({ id, data }) => {
173195
<tbody>
174196
{data.headers.map((pair, index) => (
175197
<tr key={index} className='text-ghost-700 hover:bg-ghost-50 border-b border-gray-200 text-sm'>
176-
<td className='whitespace-no-wrap border-2 border-background-dark'>
177-
<input
178-
type='text'
179-
className='nodrag nowheel block h-9 w-full bg-background-light p-2.5 outline-none'
198+
<td className='whitespace-no-wrap w-[45%] border-2 border-background-dark'>
199+
<TextEditor
200+
placeHolder=''
201+
onChangeHandler={(name) => headerNameChange(index, name)}
180202
name='header-name'
181203
value={pair.name}
182-
onChange={(e) => {
183-
const existingHeaders = [...data.headers];
184-
existingHeaders[index].name = e.target.value;
185-
setRequestNodeHeaders(id, existingHeaders);
186-
}}
204+
completionOptions={getActiveVariables()}
205+
styles={'w-40 nodrag nowheel block bg-background-light p-2.5 outline-none'}
187206
/>
188207
</td>
189-
<td className='whitespace-no-wrap border-2 border-background-dark'>
190-
<input
191-
type='text'
192-
className='nodrag nowheel block h-9 w-full bg-background-light p-2.5 outline-none'
208+
<td className='whitespace-no-wrap w-[45%] border-2 border-background-dark'>
209+
<TextEditor
210+
placeHolder=''
211+
onChangeHandler={(value) => headerValueChange(index, value)}
193212
name='header-value'
194-
data-type='text'
195-
onChange={(e) => {
196-
const existingHeaders = [...data.headers];
197-
existingHeaders[index].value = e.target.value;
198-
setRequestNodeHeaders(id, existingHeaders);
199-
}}
200213
value={pair.value}
214+
completionOptions={getActiveVariables()}
215+
styles={'w-40 nodrag nowheel block bg-background-light p-2.5 outline-none'}
201216
/>
202217
</td>
203-
<td className='border-2 border-background-dark p-2'>
218+
<td className='w-[10%] border-2 border-background-dark p-2'>
204219
<div className='flex items-center gap-4'>
205220
{/* <Tooltip text={variables[id].type} /> */}
206221
<div
@@ -320,7 +335,9 @@ const RequestNode = ({ id, data }) => {
320335
name={'url'}
321336
value={data.url ? data.url : ''}
322337
completionOptions={getActiveVariables()}
323-
styles={'w-full mb-2'}
338+
styles={
339+
'w-full mb-2 nodrag nowheel rounded block border border-slate-700 bg-background-light p-2.5 text-base outline-none'
340+
}
324341
/>
325342
<NodeHorizontalDivider />
326343
<Tab.Group defaultIndex={getDefaultIndex()}>

0 commit comments

Comments
 (0)