Skip to content

Commit 1f0011e

Browse files
authored
Merge pull request #136 from FlowTestAI/bearer-token-auth
feat: Add support for bearer token auth type
2 parents 3cc10a7 + 178dcdd commit 1f0011e

15 files changed

Lines changed: 235 additions & 110 deletions

File tree

.changeset/honest-bags-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'flowtestai-app': minor
3+
---
4+
5+
add support for bearer token auth type

packages/flowtest-cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ If you need to use an environment, you can specify it with the `--env` or `-e` o
3838
flow run -f test.flow -e environments/test.env
3939
```
4040

41-
If you need to publish the results of your flow runs for further analysis, you can specify the `-s` option. Request your access key pairs from https://flowtest-ai.vercel.app/ and then run export $FLOWTEST_ACCESS_ID and $FLOWTEST_ACCESS_KEY before publishing:
41+
If you need to publish the results of your flow runs for further analysis, you can specify the `-s` option. Request your access key pairs from https://www.useflowtest.ai/ and then run export $FLOWTEST_ACCESS_ID and $FLOWTEST_ACCESS_KEY before publishing:
4242

4343
```bash
4444
flow run -f test.flow -e environments/test.env -s

packages/flowtest-cli/bin/axiosClient.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const axios = require('axios');
33
const axiosRetry = require('axios-retry').default;
44

5-
const baseUrl = 'https://flowtest-ai.vercel.app';
5+
const baseUrl = 'https://www.useflowtest.ai';
66

77
const axiosClient = axios.create({
88
baseURL: `${baseUrl}/api`,

packages/flowtest-cli/bin/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ const argv = yargs(hideBin(process.argv))
142142
console.log('\n');
143143
console.log(
144144
chalk.yellow(
145-
'Enable flow scans today to get more value our of your APIs. Get your access key pairs at https://flowtest-ai.vercel.app/ \n',
145+
'Enable flow scans today to get more value our of your APIs. Get your access key pairs at https://www.useflowtest.ai/ \n',
146146
),
147147
);
148148
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ class authNode extends Node {
2323
username,
2424
password,
2525
};
26+
} else if (this.nodeData.type === 'bearer-token') {
27+
this.logger.add(LogLevel.INFO, '', { type: 'authNode', data: { authType: 'Bearer Token' } });
28+
const token = computeVariables(this.nodeData.token, this.envVariables);
29+
return {
30+
type: 'bearer-token',
31+
token,
32+
};
2633
} else if (this.nodeData.type === 'no-auth') {
2734
console.log(chalk.green(` ✓ `) + chalk.dim('.....using no authentication'));
2835
this.logger.add(LogLevel.INFO, '', { type: 'authNode', data: { authType: 'No Authentication' } });

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,18 @@ class requestNode extends Node {
123123
});
124124
}
125125

126+
if (this.auth && this.auth?.type === 'bearer-token') {
127+
headers['Authorization'] = `Bearer ${this.auth.token}`;
128+
}
129+
126130
const options = {
127131
method: restMethod,
128132
url: finalUrl,
129133
headers,
130134
data: requestData,
131135
};
132136

133-
if (this.auth && this.auth.type === 'basic-auth') {
137+
if (this.auth && this.auth?.type === 'basic-auth') {
134138
options.auth = {};
135139
options.auth.username = this.auth.username;
136140
options.auth.password = this.auth.password;

src/components/molecules/environment/index.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,36 @@ const Env = ({ tab }) => {
3434

3535
return (
3636
<div className='p-4' key={tab.id}>
37-
<table className='w-full leading-normal'>
37+
<table className='w-full table-fixed leading-normal'>
3838
<thead>
3939
<tr className='bg-ghost-50 text-ghost-600 text-left text-xs font-bold uppercase tracking-wider'>
40-
<th className='border-ghost-200 max-w-4 border p-5'>S. No.</th>
41-
<th className='border-ghost-200 border p-5 '>Key</th>
42-
<th className='border-ghost-200 border p-5'>Value</th>
43-
<th className='border-ghost-200 max-w-4 border p-5'>Action</th>
40+
<th className='border-ghost-200 border p-5' style={{ width: '50px' }}>
41+
S. No.
42+
</th>
43+
<th className='border-ghost-200 border p-5' style={{ width: '100px' }}>
44+
Key
45+
</th>
46+
<th className='border-ghost-200 border p-5' style={{ width: '200px' }}>
47+
Value
48+
</th>
49+
<th className='border-ghost-200 border p-5' style={{ width: '50px' }}>
50+
Action
51+
</th>
4452
</tr>
4553
</thead>
4654
<tbody>
4755
{Object.entries(variables).map(([key, value], index) => (
4856
<tr key={index} className='text-ghost-700 hover:bg-ghost-50 border-b border-gray-200 text-sm'>
49-
<td className='whitespace-no-wrap max-w-4 p-5'>{index + 1}</td>
50-
<td className='whitespace-no-wrap p-5'>{key}</td>
51-
<td className='whitespace-no-wrap p-5'>{value}</td>
52-
<td className='whitespace-no-wrap max-w-4 p-5'>
57+
<td className='whitespace-no-wrap p-5' style={{ width: '50px' }}>
58+
{index + 1}
59+
</td>
60+
<td className='whitespace-no-wrap truncate p-5' style={{ width: '100px' }}>
61+
{key}
62+
</td>
63+
<td className='whitespace-no-wrap truncate p-5' style={{ width: '200px' }}>
64+
{value}
65+
</td>
66+
<td className='whitespace-no-wrap p-5' style={{ width: '50px' }}>
5367
<div
5468
className='hover:bg-ghost-200 relative inline-block cursor-pointer rounded-md p-2 text-left transition duration-200 ease-out'
5569
onClick={() => {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ class authNode extends Node {
2121
username,
2222
password,
2323
};
24+
} else if (this.nodeData.type === 'bearer-token') {
25+
this.logger.add(LogLevel.INFO, '', { type: 'authNode', data: { authType: 'Bearer Token' } });
26+
const token = computeVariables(this.nodeData.token, this.envVariables);
27+
return {
28+
type: 'bearer-token',
29+
token,
30+
};
2431
} else if (this.nodeData.type === 'no-auth') {
2532
this.logger.add(LogLevel.INFO, '', { type: 'authNode', data: { authType: 'No Authentication' } });
2633
return {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ class requestNode extends Node {
103103
});
104104
}
105105

106+
if (this.auth && this.auth?.type === 'bearer-token') {
107+
headers['Authorization'] = `Bearer ${this.auth.token}`;
108+
}
109+
106110
const options = {
107111
method: restMethod,
108112
url: finalUrl,

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

Lines changed: 133 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@ import { cloneDeep } from 'lodash';
1313
const AuthNode = ({ id, data }) => {
1414
const setAuthNodeType = useCanvasStore((state) => state.setAuthNodeType);
1515
const setBasicAuthValues = useCanvasStore((state) => state.setBasicAuthValues);
16-
const [selected, setSelected] = useState(data.type && data.type === 'basic-auth' ? 'basic-auth' : 'no-auth');
16+
const setBearerTokenValue = useCanvasStore((state) => state.setBearerTokenValue);
17+
const [selected, setSelected] = useState(data.type ? data.type : 'no-auth');
1718

18-
const handleChange = (value, option) => {
19+
const handleBasicAuthValueChange = (value, option) => {
1920
setBasicAuthValues(id, option, value);
2021
};
2122

23+
const handleBearerTokenChange = (value) => {
24+
setBearerTokenValue(id, value);
25+
};
26+
2227
const getActiveVariables = () => {
2328
const collectionId = useCanvasStore.getState().collectionId;
2429
if (collectionId) {
@@ -33,6 +38,16 @@ const AuthNode = ({ id, data }) => {
3338
return [];
3439
};
3540

41+
const getAuthType = () => {
42+
if (selected === 'no-auth') {
43+
return 'No Auth';
44+
} else if (selected === 'basic-auth') {
45+
return 'Basic Auth';
46+
} else if (selected === 'bearer-token') {
47+
return 'Bearer Token';
48+
}
49+
};
50+
3651
return (
3752
<>
3853
<FlowNode
@@ -42,89 +57,122 @@ const AuthNode = ({ id, data }) => {
4257
handleRight={true}
4358
handleRightData={{ type: 'source' }}
4459
>
45-
<Listbox
46-
value={selected}
47-
onChange={(selectedValue) => {
48-
setSelected(selectedValue);
49-
setAuthNodeType(id, selectedValue);
50-
}}
51-
>
52-
<div className='relative min-w-36'>
53-
<Listbox.Button className='relative w-full p-2 text-left border rounded cursor-default border-cyan-950'>
54-
<span className='block truncate'>{selected === 'no-auth' ? 'No Auth' : 'Basic Auth'}</span>
55-
<span className='absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none'>
56-
<ChevronUpDownIcon className='w-5 h-5' aria-hidden='true' />
57-
</span>
58-
</Listbox.Button>
59-
<Transition
60-
as={Fragment}
61-
leave='transition ease-in duration-100'
62-
leaveFrom='opacity-100'
63-
leaveTo='opacity-0'
64-
>
65-
<Listbox.Options className='absolute z-10 w-full py-1 mt-1 overflow-auto text-base bg-white max-h-60 focus:outline-none'>
66-
<Listbox.Option
67-
className={({ active }) =>
68-
`relative cursor-default select-none py-2 pl-10 pr-4 hover:font-semibold ${
69-
active ? 'bg-background-light text-slate-900' : ''
70-
}`
71-
}
72-
value={'no-auth'}
73-
>
74-
{({ selected }) => (
75-
<>
76-
<span className={`block`}>No Auth</span>
77-
{selected ? (
78-
<span className='absolute inset-y-0 left-0 flex items-center pl-3 font-semibold'>
79-
<CheckIcon className='w-5 h-5' aria-hidden='true' />
80-
</span>
81-
) : null}
82-
</>
83-
)}
84-
</Listbox.Option>
85-
<Listbox.Option
86-
className={({ active }) =>
87-
`relative cursor-default select-none py-2 pl-10 pr-4 hover:font-semibold ${
88-
active ? 'bg-background-light text-slate-900' : ''
89-
}`
90-
}
91-
value={'basic-auth'}
92-
>
93-
{({ selected }) => (
94-
<>
95-
<span className={`block`}>Basic auth</span>
96-
{selected ? (
97-
<span className='absolute inset-y-0 left-0 flex items-center pl-3 font-semibold'>
98-
<CheckIcon className='w-5 h-5' aria-hidden='true' />
99-
</span>
100-
) : null}
101-
</>
102-
)}
103-
</Listbox.Option>
104-
</Listbox.Options>
105-
</Transition>
106-
</div>
107-
</Listbox>
108-
{data.type === 'basic-auth' && (
109-
<div className='flex flex-col gap-2 py-4'>
110-
<TextEditor
111-
placeHolder={`Username`}
112-
onChangeHandler={(value) => handleChange(value, 'username')}
113-
name={'username'}
114-
value={data.username ? data.username : ''}
115-
completionOptions={getActiveVariables()}
116-
styles={'w-full'}
117-
/>
118-
<TextEditor
119-
placeHolder={`Password`}
120-
onChangeHandler={(value) => handleChange(value, 'password')}
121-
name={'password'}
122-
value={data.password ? data.password : ''}
123-
completionOptions={getActiveVariables()}
124-
styles={'w-full'}
125-
/>
126-
</div>
127-
)}
60+
<div className='w-52'>
61+
<Listbox
62+
value={selected}
63+
onChange={(selectedValue) => {
64+
setSelected(selectedValue);
65+
setAuthNodeType(id, selectedValue);
66+
}}
67+
>
68+
<div className='relative'>
69+
<Listbox.Button className='relative w-full cursor-default rounded border border-cyan-950 p-2 text-left'>
70+
<span className='block truncate'>{getAuthType()}</span>
71+
<span className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2'>
72+
<ChevronUpDownIcon className='h-5 w-5' aria-hidden='true' />
73+
</span>
74+
</Listbox.Button>
75+
<Transition
76+
as={Fragment}
77+
leave='transition ease-in duration-100'
78+
leaveFrom='opacity-100'
79+
leaveTo='opacity-0'
80+
>
81+
<Listbox.Options className='absolute z-10 mt-1 max-h-60 w-full overflow-auto bg-white py-1 text-base focus:outline-none'>
82+
<Listbox.Option
83+
className={({ active }) =>
84+
`relative cursor-default select-none py-2 pl-10 pr-4 hover:font-semibold ${
85+
active ? 'bg-background-light text-slate-900' : ''
86+
}`
87+
}
88+
value={'no-auth'}
89+
>
90+
{({ selected }) => (
91+
<>
92+
<span className={`block`}>No Auth</span>
93+
{selected ? (
94+
<span className='absolute inset-y-0 left-0 flex items-center pl-3 font-semibold'>
95+
<CheckIcon className='h-5 w-5' aria-hidden='true' />
96+
</span>
97+
) : null}
98+
</>
99+
)}
100+
</Listbox.Option>
101+
<Listbox.Option
102+
className={({ active }) =>
103+
`relative cursor-default select-none py-2 pl-10 pr-4 hover:font-semibold ${
104+
active ? 'bg-background-light text-slate-900' : ''
105+
}`
106+
}
107+
value={'basic-auth'}
108+
>
109+
{({ selected }) => (
110+
<>
111+
<span className={`block`}>Basic auth</span>
112+
{selected ? (
113+
<span className='absolute inset-y-0 left-0 flex items-center pl-3 font-semibold'>
114+
<CheckIcon className='h-5 w-5' aria-hidden='true' />
115+
</span>
116+
) : null}
117+
</>
118+
)}
119+
</Listbox.Option>
120+
<Listbox.Option
121+
className={({ active }) =>
122+
`relative cursor-default select-none py-2 pl-10 pr-4 hover:font-semibold ${
123+
active ? 'bg-background-light text-slate-900' : ''
124+
}`
125+
}
126+
value={'bearer-token'}
127+
>
128+
{({ selected }) => (
129+
<>
130+
<span className={`block`}>Bearer Token</span>
131+
{selected ? (
132+
<span className='absolute inset-y-0 left-0 flex items-center pl-3 font-semibold'>
133+
<CheckIcon className='h-5 w-5' aria-hidden='true' />
134+
</span>
135+
) : null}
136+
</>
137+
)}
138+
</Listbox.Option>
139+
</Listbox.Options>
140+
</Transition>
141+
</div>
142+
</Listbox>
143+
{data.type === 'basic-auth' && (
144+
<div className='flex flex-col gap-2 py-4'>
145+
<TextEditor
146+
placeHolder={`Username`}
147+
onChangeHandler={(value) => handleBasicAuthValueChange(value, 'username')}
148+
name={'username'}
149+
value={data.username ? data.username : ''}
150+
completionOptions={getActiveVariables()}
151+
styles={'w-full'}
152+
/>
153+
<TextEditor
154+
placeHolder={`Password`}
155+
onChangeHandler={(value) => handleBasicAuthValueChange(value, 'password')}
156+
name={'password'}
157+
value={data.password ? data.password : ''}
158+
completionOptions={getActiveVariables()}
159+
styles={'w-full'}
160+
/>
161+
</div>
162+
)}
163+
{data.type === 'bearer-token' && (
164+
<div className='flex flex-col gap-2 py-4'>
165+
<TextEditor
166+
placeHolder={`Token`}
167+
onChangeHandler={(value) => handleBearerTokenChange(value)}
168+
name={'token'}
169+
value={data.token ? data.token : ''}
170+
completionOptions={getActiveVariables()}
171+
styles={'w-full'}
172+
/>
173+
</div>
174+
)}
175+
</div>
128176
</FlowNode>
129177
</>
130178
);

0 commit comments

Comments
 (0)