mirror of
https://github.com/usebruno/bruno.git
synced 2025-02-08 14:02:09 +01:00
Merge branch 'main' into bugfix/remove-disclaimer
This commit is contained in:
commit
27ac50b31f
41
package-lock.json
generated
41
package-lock.json
generated
@ -8184,6 +8184,29 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios-ntlm": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.3.tgz",
|
||||
"integrity": "sha512-CS6WE8chZpEDKxv4IFwr5zcG7InMC6Ek0aj2n2tHauBh+8KiYVC4qMn3N2arjR5tnyILQuTGlI0mc83hgWxS4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"des.js": "^1.1.0",
|
||||
"dev-null": "^0.1.1",
|
||||
"js-md4": "^0.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/axios-ntlm/node_modules/axios": {
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
@ -10928,7 +10951,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz",
|
||||
"integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
@ -10979,6 +11001,12 @@
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dev-null": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz",
|
||||
"integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
@ -15466,6 +15494,12 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-md4": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
|
||||
"integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@ -16722,7 +16756,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/minimalistic-crypto-utils": {
|
||||
@ -24115,6 +24148,7 @@
|
||||
"@usebruno/vm2": "^3.9.13",
|
||||
"aws4-axios": "^3.3.0",
|
||||
"axios": "1.7.5",
|
||||
"axios-ntlm": "^1.4.2",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
"decomment": "^0.9.5",
|
||||
@ -24165,7 +24199,7 @@
|
||||
},
|
||||
"packages/bruno-electron": {
|
||||
"name": "bruno",
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.38.1",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "3.658.1",
|
||||
"@usebruno/common": "0.1.0",
|
||||
@ -24177,6 +24211,7 @@
|
||||
"about-window": "^1.15.2",
|
||||
"aws4-axios": "^3.3.0",
|
||||
"axios": "1.7.5",
|
||||
"axios-ntlm": "^1.4.2",
|
||||
"chai": "^4.3.7",
|
||||
"chokidar": "^3.5.3",
|
||||
"content-disposition": "^0.5.4",
|
||||
|
@ -24,4 +24,16 @@ export default defineConfig({
|
||||
html: {
|
||||
title: 'Bruno'
|
||||
},
|
||||
tools: {
|
||||
rspack: {
|
||||
module: {
|
||||
parser: {
|
||||
javascript: {
|
||||
// This loads the JavaScript contents from a library along with the main JavaScript bundle.
|
||||
dynamicImportMode: "eager",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
@ -7,12 +7,12 @@
|
||||
|
||||
import React from 'react';
|
||||
import { isEqual, escapeRegExp } from 'lodash';
|
||||
import { getEnvironmentVariables } from 'utils/collections';
|
||||
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import * as jsonlint from '@prantlf/jsonlint';
|
||||
import { JSHINT } from 'jshint';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
import { getAllVariables } from 'utils/collections';
|
||||
|
||||
let CodeMirror;
|
||||
const SERVER_RENDERED = typeof window === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||
@ -293,7 +293,7 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
|
||||
if (this.editor) {
|
||||
let variables = getEnvironmentVariables(this.props.collection);
|
||||
let variables = getAllVariables(this.props.collection, this.props.item);
|
||||
if (!isEqual(variables, this.variables)) {
|
||||
this.addOverlay();
|
||||
}
|
||||
@ -333,7 +333,7 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
addOverlay = () => {
|
||||
const mode = this.props.mode || 'application/ld+json';
|
||||
let variables = getEnvironmentVariables(this.props.collection);
|
||||
let variables = getAllVariables(this.props.collection, this.props.item);
|
||||
this.variables = variables;
|
||||
|
||||
defineCodeMirrorBrunoVariablesMode(variables, mode);
|
||||
|
@ -79,6 +79,15 @@ const AuthMode = ({ collection }) => {
|
||||
>
|
||||
Digest Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onModeChange('ntlm');
|
||||
}}
|
||||
>
|
||||
NTLM Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
|
@ -0,0 +1,17 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
max-width: 400px;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const NTLMAuth = ({ collection }) => {
|
||||
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const ntlmAuth = get(collection, 'root.request.auth.ntlm', {});
|
||||
|
||||
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||
|
||||
|
||||
const handleUsernameChange = (username) => {
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
username: username,
|
||||
password: ntlmAuth.password,
|
||||
domain: ntlmAuth.domain
|
||||
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (password) => {
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
username: ntlmAuth.username,
|
||||
password: password,
|
||||
domain: ntlmAuth.domain
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleDomainChange = (domain) => {
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
username: ntlmAuth.username,
|
||||
password: ntlmAuth.password,
|
||||
domain: domain
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Username</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.username || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleUsernameChange(val)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Password</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.password || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handlePasswordChange(val)}
|
||||
collection={collection}
|
||||
isSecret={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Domain</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.domain || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleDomainChange(val)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default NTLMAuth;
|
@ -11,6 +11,8 @@ import ApiKeyAuth from './ApiKeyAuth/';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import OAuth2 from './OAuth2';
|
||||
import NTLMAuth from './NTLMAuth';
|
||||
|
||||
|
||||
const Auth = ({ collection }) => {
|
||||
const authMode = get(collection, 'root.request.auth.mode');
|
||||
@ -32,6 +34,9 @@ const Auth = ({ collection }) => {
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} />;
|
||||
}
|
||||
case 'ntlm': {
|
||||
return <NTLMAuth collection={collection} />;
|
||||
}
|
||||
case 'oauth2': {
|
||||
return <OAuth2 collection={collection} />;
|
||||
}
|
||||
|
@ -2,9 +2,6 @@ import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
div.CodeMirror {
|
||||
/* todo: find a better way */
|
||||
height: calc(100vh - 240px);
|
||||
|
||||
.CodeMirror-scroll {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
@ -32,22 +32,27 @@ const Docs = ({ collection }) => {
|
||||
const onSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-1 h-full w-full relative">
|
||||
<div className="editing-mode mb-2" role="tab" onClick={toggleViewMode}>
|
||||
<StyledWrapper className="mt-1 h-full w-full relative flex flex-col">
|
||||
<div className="editing-mode flex justify-between items-center" role="tab" onClick={toggleViewMode}>
|
||||
{isEditing ? 'Preview' : 'Edit'}
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
value={docs || ''}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
mode="application/text"
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
/>
|
||||
<div className="mt-2 flex-1 max-h-[70vh]">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
value={docs || ''}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
mode="application/text"
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
/>
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary my-6" onClick={onSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
|
||||
)}
|
||||
|
@ -78,7 +78,10 @@ const EnvironmentSelector = ({ collection }) => {
|
||||
<IconDatabaseOff size={18} strokeWidth={1.5} />
|
||||
<span className="ml-2">No Environment</span>
|
||||
</div>
|
||||
<div className="dropdown-item border-top" onClick={handleSettingsIconClick}>
|
||||
<div className="dropdown-item border-top" onClick={() => {
|
||||
handleSettingsIconClick();
|
||||
dropdownTippyRef.current.hide();
|
||||
}}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconSettings size={18} strokeWidth={1.5} />
|
||||
</div>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { IconTrash, IconAlertCircle } from '@tabler/icons';
|
||||
import { IconTrash, IconAlertCircle, IconDeviceFloppy, IconRefresh, IconCircleCheck } from '@tabler/icons';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { selectEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { uuid } from 'utils/common';
|
||||
@ -13,7 +14,7 @@ import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions
|
||||
import toast from 'react-hot-toast';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
|
||||
const EnvironmentVariables = ({ environment, collection, setIsModified, originalEnvironmentVariables }) => {
|
||||
const EnvironmentVariables = ({ environment, collection, setIsModified, originalEnvironmentVariables, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
const addButtonRef = useRef(null);
|
||||
@ -84,6 +85,19 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original
|
||||
formik.setFieldValue(formik.values.length, newVariable, false);
|
||||
};
|
||||
|
||||
const onActivate = () => {
|
||||
dispatch(selectEnvironment(environment ? environment.uid : null, collection.uid))
|
||||
.then(() => {
|
||||
if (environment) {
|
||||
toast.success(`Environment changed to ${environment.name}`);
|
||||
onClose();
|
||||
} else {
|
||||
toast.success(`No Environments are active now`);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err) && toast.error('An error occurred while selecting the environment'));
|
||||
};
|
||||
|
||||
const handleRemoveVar = (id) => {
|
||||
formik.setValues(formik.values.filter((variable) => variable.uid !== id));
|
||||
};
|
||||
@ -183,13 +197,19 @@ const EnvironmentVariables = ({ environment, collection, setIsModified, original
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" className="submit btn btn-md btn-secondary mt-2" onClick={formik.handleSubmit}>
|
||||
<div className="flex items-center">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary mt-2 flex items-center" onClick={formik.handleSubmit}>
|
||||
<IconDeviceFloppy size={16} strokeWidth={1.5} className="mr-1" />
|
||||
Save
|
||||
</button>
|
||||
<button type="submit" className="ml-2 px-1 submit btn btn-md btn-secondary mt-2" onClick={handleReset}>
|
||||
<button type="submit" className="ml-2 px-1 submit btn btn-sm btn-close mt-2 flex items-center" onClick={handleReset}>
|
||||
<IconRefresh size={16} strokeWidth={1.5} className="mr-1" />
|
||||
Reset
|
||||
</button>
|
||||
<button type="submit" className="submit btn btn-sm btn-close mt-2 flex items-center" onClick={onActivate}>
|
||||
<IconCircleCheck size={16} strokeWidth={1.5} className="mr-1" />
|
||||
Activate
|
||||
</button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ import DeleteEnvironment from '../../DeleteEnvironment';
|
||||
import RenameEnvironment from '../../RenameEnvironment';
|
||||
import EnvironmentVariables from './EnvironmentVariables';
|
||||
|
||||
const EnvironmentDetails = ({ environment, collection, setIsModified }) => {
|
||||
const EnvironmentDetails = ({ environment, collection, setIsModified, onClose }) => {
|
||||
const [openEditModal, setOpenEditModal] = useState(false);
|
||||
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
||||
const [openCopyModal, setOpenCopyModal] = useState(false);
|
||||
@ -38,7 +38,7 @@ const EnvironmentDetails = ({ environment, collection, setIsModified }) => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<EnvironmentVariables environment={environment} collection={collection} setIsModified={setIsModified} />
|
||||
<EnvironmentVariables environment={environment} collection={collection} setIsModified={setIsModified} onClose={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -9,8 +9,9 @@ import ManageSecrets from '../ManageSecrets';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import ConfirmSwitchEnv from './ConfirmSwitchEnv';
|
||||
import ToolHint from 'components/ToolHint';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
const EnvironmentList = ({ selectedEnvironment, setSelectedEnvironment, collection, isModified, setIsModified }) => {
|
||||
const EnvironmentList = ({ selectedEnvironment, setSelectedEnvironment, collection, isModified, setIsModified, onClose }) => {
|
||||
const { environments } = collection;
|
||||
const [openCreateModal, setOpenCreateModal] = useState(false);
|
||||
const [openImportModal, setOpenImportModal] = useState(false);
|
||||
@ -24,6 +25,11 @@ const EnvironmentList = ({ selectedEnvironment, setSelectedEnvironment, collecti
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEnvironment) {
|
||||
const _selectedEnvironment = environments?.find(env => env?.uid === selectedEnvironment?.uid);
|
||||
const hasSelectedEnvironmentChanged = !isEqual(selectedEnvironment, _selectedEnvironment);
|
||||
if (hasSelectedEnvironmentChanged) {
|
||||
setSelectedEnvironment(_selectedEnvironment);
|
||||
}
|
||||
setOriginalEnvironmentVariables(selectedEnvironment.variables);
|
||||
return;
|
||||
}
|
||||
@ -135,6 +141,7 @@ const EnvironmentList = ({ selectedEnvironment, setSelectedEnvironment, collecti
|
||||
collection={collection}
|
||||
setIsModified={setIsModified}
|
||||
originalEnvironmentVariables={originalEnvironmentVariables}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
@ -72,6 +72,7 @@ const EnvironmentSettings = ({ collection, onClose }) => {
|
||||
collection={collection}
|
||||
isModified={isModified}
|
||||
setIsModified={setIsModified}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
|
@ -37,22 +37,25 @@ const Documentation = ({ collection, folder }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="flex flex-col gap-y-1 h-full w-full relative">
|
||||
<div className="editing-mode" role="tab" onClick={toggleViewMode}>
|
||||
<StyledWrapper className="mt-1 h-full w-full relative flex flex-col">
|
||||
<div className="editing-mode flex justify-between items-center" role="tab" onClick={toggleViewMode}>
|
||||
{isEditing ? 'Preview' : 'Edit'}
|
||||
</div>
|
||||
|
||||
{isEditing ? (
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
value={docs || ''}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
mode="application/text"
|
||||
/>
|
||||
<div className="mt-2 flex-1 max-h-[70vh]">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
value={docs || ''}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
mode="application/text"
|
||||
/>
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary my-6" onClick={onSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
|
||||
)}
|
||||
|
@ -55,7 +55,7 @@ const StyledMarkdownBodyWrapper = styled.div`
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: var(--color-border-default);
|
||||
background-color: var(--color-sidebar-collection-item-active-indent-border);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ const Modal = ({
|
||||
confirmText,
|
||||
cancelText,
|
||||
handleCancel,
|
||||
handleConfirm,
|
||||
handleConfirm = () => {},
|
||||
children,
|
||||
confirmDisabled,
|
||||
hideCancel,
|
||||
@ -92,7 +92,7 @@ const Modal = ({
|
||||
};
|
||||
|
||||
useFocusTrap(modalRef);
|
||||
|
||||
|
||||
const closeModal = (args) => {
|
||||
setIsClosing(true);
|
||||
setTimeout(() => handleCancel(args), closeModalFadeTimeout);
|
||||
@ -103,7 +103,7 @@ const Modal = ({
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeydown);
|
||||
};
|
||||
}, [disableEscapeKey, document]);
|
||||
}, [disableEscapeKey, document, handleConfirm]);
|
||||
|
||||
let classes = 'bruno-modal';
|
||||
if (isClosing) {
|
||||
|
@ -19,9 +19,8 @@ const StyledWrapper = styled.div`
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: hidden !important;
|
||||
${'' /* padding-bottom: 50px !important; */}
|
||||
.CodeMirror-scroll {
|
||||
overflow: visible !important;
|
||||
position: relative;
|
||||
display: block;
|
||||
margin: 0px;
|
||||
|
@ -146,19 +146,8 @@ const AssertionRow = ({
|
||||
const { operator, value } = parseAssertionOperator(assertion.value);
|
||||
|
||||
return (
|
||||
<tr key={assertion.uid}>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={assertion.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleAssertionChange(e, assertion, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<>
|
||||
|
||||
<td>
|
||||
<AssertionOperator
|
||||
operator={operator}
|
||||
@ -214,7 +203,7 @@ const AssertionRow = ({
|
||||
<RemoveButton onClick={() => handleRemoveAssertion(assertion)} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@ const Wrapper = styled.div`
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-weight: 600;
|
||||
table-layout: fixed;
|
||||
|
||||
thead,
|
||||
@ -15,24 +16,15 @@ const Wrapper = styled.div`
|
||||
color: ${(props) => props.theme.table.thead.color};
|
||||
font-size: 0.8125rem;
|
||||
user-select: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
select {
|
||||
select {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-add-assertion {
|
||||
font-size: 0.8125rem;
|
||||
@ -42,7 +34,8 @@ const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
border: solid 1px transparent;
|
||||
outline: none !important;
|
||||
background-color: inherit;
|
||||
color: ${(props) => props.theme.table.input.color};
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
|
@ -6,6 +6,9 @@ import { addAssertion, updateAssertion, deleteAssertion } from 'providers/ReduxS
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import AssertionRow from './AssertionRow';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import Table from 'components/Table/index';
|
||||
import ReorderTable from 'components/ReorderTable/index';
|
||||
import { moveAssertion } from 'providers/ReduxStore/slices/collections/index';
|
||||
|
||||
const Assertions = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@ -57,21 +60,43 @@ const Assertions = ({ item, collection }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleAssertionDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveAssertion({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
updateReorderedItem
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Expr</td>
|
||||
<td>Operator</td>
|
||||
<td>Value</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<Table
|
||||
headers={[
|
||||
{ name: 'Expr', accessor: 'expr', width: '30%' },
|
||||
{ name: 'Operator', accessor: 'operator', width: '120px' },
|
||||
{ name: 'Value', accessor: 'value', width: '30%' },
|
||||
{ name: '', accessor: '', width: '15%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleAssertionDrag}>
|
||||
{assertions && assertions.length
|
||||
? assertions.map((assertion) => {
|
||||
return (
|
||||
return (
|
||||
<tr key={assertion.uid} data-uid={assertion.uid}>
|
||||
<td className='flex relative'>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={assertion.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleAssertionChange(e, assertion, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<AssertionRow
|
||||
key={assertion.uid}
|
||||
assertion={assertion}
|
||||
@ -82,11 +107,12 @@ const Assertions = ({ item, collection }) => {
|
||||
onSave={onSave}
|
||||
handleRun={handleRun}
|
||||
/>
|
||||
);
|
||||
})
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</ReorderTable>
|
||||
</Table>
|
||||
<button className="btn-add-assertion text-link pr-2 py-3 mt-2 select-none" onClick={handleAddAssertion}>
|
||||
+ Add Assertion
|
||||
</button>
|
||||
|
@ -70,6 +70,15 @@ const AuthMode = ({ item, collection }) => {
|
||||
>
|
||||
Digest Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef?.current?.hide();
|
||||
onModeChange('ntlm');
|
||||
}}
|
||||
>
|
||||
NTLM Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
|
@ -0,0 +1,17 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
max-width: 400px;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const NTLMAuth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const ntlmAuth = item.draft ? get(item, 'draft.request.auth.ntlm', {}) : get(item, 'request.auth.ntlm', {});
|
||||
|
||||
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const handleUsernameChange = (username) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
username: username,
|
||||
password: ntlmAuth.password,
|
||||
domain: ntlmAuth.domain
|
||||
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (password) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
username: ntlmAuth.username,
|
||||
password: password,
|
||||
domain: ntlmAuth.domain
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleDomainChange = (domain) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'ntlm',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
username: ntlmAuth.username,
|
||||
password: ntlmAuth.password,
|
||||
domain: domain
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Username</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.username || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleUsernameChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Password</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.password || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handlePasswordChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
isSecret={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Domain</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={ntlmAuth.domain || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleDomainChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default NTLMAuth;
|
@ -6,6 +6,8 @@ import BearerAuth from './BearerAuth';
|
||||
import BasicAuth from './BasicAuth';
|
||||
import DigestAuth from './DigestAuth';
|
||||
import WsseAuth from './WsseAuth';
|
||||
import NTLMAuth from './NTLMAuth';
|
||||
|
||||
import ApiKeyAuth from './ApiKeyAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { humanizeRequestAuthMode } from 'utils/collections/index';
|
||||
@ -31,6 +33,9 @@ const Auth = ({ item, collection }) => {
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} item={item} />;
|
||||
}
|
||||
case 'ntlm': {
|
||||
return <NTLMAuth collection={collection} item={item} />;
|
||||
}
|
||||
case 'oauth2': {
|
||||
return <OAuth2 collection={collection} item={item} />;
|
||||
}
|
||||
|
@ -19,16 +19,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-add-param {
|
||||
font-size: 0.8125rem;
|
||||
|
@ -6,12 +6,15 @@ import { useTheme } from 'providers/Theme';
|
||||
import {
|
||||
addFormUrlEncodedParam,
|
||||
updateFormUrlEncodedParam,
|
||||
deleteFormUrlEncodedParam
|
||||
deleteFormUrlEncodedParam,
|
||||
moveFormUrlEncodedParam
|
||||
} from 'providers/ReduxStore/slices/collections';
|
||||
import MultiLineEditor from 'components/MultiLineEditor';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import RemoveButton from 'components/RemoveButton/index';
|
||||
import ReorderTable from 'components/ReorderTable/index';
|
||||
import Table from 'components/Table/index';
|
||||
|
||||
const FormUrlEncodedParams = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@ -64,55 +67,64 @@ const FormUrlEncodedParams = ({ item, collection }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleParamDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveFormUrlEncodedParam({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
updateReorderedItem
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Key</td>
|
||||
<td>Value</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<Table
|
||||
headers={[
|
||||
{ name: 'Key', accessor: 'key', width: '40%' },
|
||||
{ name: 'Value', accessor: 'value', width: '46%' },
|
||||
{ name: '', accessor: '', width: '14%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleParamDrag}>
|
||||
{params && params.length
|
||||
? params.map((param, index) => {
|
||||
return (
|
||||
<tr key={param.uid}>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={param.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleParamChange(e, param, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<MultiLineEditor
|
||||
value={param.value}
|
||||
theme={storedTheme}
|
||||
onSave={onSave}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'value'
|
||||
)
|
||||
}
|
||||
allowNewlines={true}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
</td>
|
||||
return (
|
||||
<tr key={param.uid} data-uid={param.uid}>
|
||||
<td className='flex relative'>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={param.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleParamChange(e, param, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<MultiLineEditor
|
||||
value={param.value}
|
||||
theme={storedTheme}
|
||||
onSave={onSave}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'value'
|
||||
)
|
||||
}
|
||||
allowNewlines={true}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
@ -129,8 +141,8 @@ const FormUrlEncodedParams = ({ item, collection }) => {
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</ReorderTable>
|
||||
</Table>
|
||||
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addParam}>
|
||||
+ Add Param
|
||||
</button>
|
||||
|
@ -19,23 +19,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-add-param {
|
||||
|
@ -6,13 +6,16 @@ import { useTheme } from 'providers/Theme';
|
||||
import {
|
||||
addMultipartFormParam,
|
||||
updateMultipartFormParam,
|
||||
deleteMultipartFormParam
|
||||
deleteMultipartFormParam,
|
||||
moveMultipartFormParam
|
||||
} from 'providers/ReduxStore/slices/collections';
|
||||
import MultiLineEditor from 'components/MultiLineEditor';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import FilePickerEditor from 'components/FilePickerEditor';
|
||||
import RemoveButton from 'components/RemoveButton';
|
||||
import Table from 'components/Table/index';
|
||||
import ReorderTable from 'components/ReorderTable/index';
|
||||
|
||||
const MultipartFormParams = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@ -82,80 +85,47 @@ const MultipartFormParams = ({ item, collection }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleParamDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveMultipartFormParam({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
updateReorderedItem
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Key</td>
|
||||
<td>Value</td>
|
||||
<td>Content-Type</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<Table
|
||||
headers={[
|
||||
{ name: 'Key', accessor: 'key', width: '29%' },
|
||||
{ name: 'Value', accessor: 'value', width: '29%' },
|
||||
{ name: 'Content-Type', accessor: 'content-type', width: '28%' },
|
||||
{ name: '', accessor: '', width: '14%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleParamDrag}>
|
||||
{params && params.length
|
||||
? params.map((param, index) => {
|
||||
return (
|
||||
<tr key={param.uid}>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={param.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleParamChange(e, param, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{param.type === 'file' ? (
|
||||
<FilePickerEditor
|
||||
value={param.value}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'value'
|
||||
)
|
||||
}
|
||||
collection={collection}
|
||||
/>
|
||||
) : (
|
||||
<MultiLineEditor
|
||||
onSave={onSave}
|
||||
theme={storedTheme}
|
||||
value={param.value}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'value'
|
||||
)
|
||||
}
|
||||
onRun={handleRun}
|
||||
allowNewlines={true}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<MultiLineEditor
|
||||
onSave={onSave}
|
||||
theme={storedTheme}
|
||||
placeholder="Auto"
|
||||
value={param.contentType}
|
||||
return (
|
||||
<tr key={param.uid} className='w-full' data-uid={param.uid}>
|
||||
<td className="flex relative">
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={param.name}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleParamChange(e, param, 'name')}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{param.type === 'file' ? (
|
||||
<FilePickerEditor
|
||||
value={param.value}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
@ -164,13 +134,55 @@ const MultipartFormParams = ({ item, collection }) => {
|
||||
}
|
||||
},
|
||||
param,
|
||||
'contentType'
|
||||
'value'
|
||||
)
|
||||
}
|
||||
collection={collection}
|
||||
/>
|
||||
) : (
|
||||
<MultiLineEditor
|
||||
onSave={onSave}
|
||||
theme={storedTheme}
|
||||
value={param.value}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'value'
|
||||
)
|
||||
}
|
||||
onRun={handleRun}
|
||||
allowNewlines={true}
|
||||
collection={collection}
|
||||
item={item}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<MultiLineEditor
|
||||
onSave={onSave}
|
||||
theme={storedTheme}
|
||||
placeholder="Auto"
|
||||
value={param.contentType}
|
||||
onChange={(newValue) =>
|
||||
handleParamChange(
|
||||
{
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
},
|
||||
param,
|
||||
'contentType'
|
||||
)
|
||||
}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
@ -187,8 +199,8 @@ const MultipartFormParams = ({ item, collection }) => {
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</ReorderTable>
|
||||
</Table>
|
||||
<div>
|
||||
<button className="btn-add-param text-link pr-2 pt-3 mt-2 select-none" onClick={addParam}>
|
||||
+ Add Param
|
||||
|
@ -103,7 +103,7 @@ const QueryParams = ({ item, collection }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleParamDrag = ({ updateReorderedItem }) => {
|
||||
const handleQueryParamDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveQueryParam({
|
||||
collectionUid: collection.uid,
|
||||
@ -124,7 +124,7 @@ const QueryParams = ({ item, collection }) => {
|
||||
{ name: '', accessor: '', width: '13%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleParamDrag}>
|
||||
<ReorderTable updateReorderedItem={handleQueryParamDrag}>
|
||||
{queryParams && queryParams.length
|
||||
? queryParams.map((param, index) => (
|
||||
<tr key={param.uid} data-uid={param.uid}>
|
||||
|
@ -48,6 +48,7 @@ const RequestBody = ({ item, collection }) => {
|
||||
<StyledWrapper className="w-full">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
item={item}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
|
@ -19,15 +19,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-add-header {
|
||||
|
@ -3,13 +3,15 @@ import get from 'lodash/get';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { addRequestHeader, updateRequestHeader, deleteRequestHeader } from 'providers/ReduxStore/slices/collections';
|
||||
import { addRequestHeader, updateRequestHeader, deleteRequestHeader, moveRequestHeader } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { headers as StandardHTTPHeaders } from 'know-your-http-well';
|
||||
import { MimeTypes } from 'utils/codemirror/autocompleteConstants';
|
||||
import RemoveButton from 'components/RemoveButton/index';
|
||||
import Table from 'components/Table/index';
|
||||
import ReorderTable from 'components/ReorderTable/index';
|
||||
const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
|
||||
|
||||
const RequestHeaders = ({ item, collection }) => {
|
||||
@ -63,22 +65,31 @@ const RequestHeaders = ({ item, collection }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleHeaderDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveRequestHeader({
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
updateReorderedItem
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Value</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{headers && headers.length
|
||||
<Table
|
||||
headers={[
|
||||
{ name: 'Key', accessor: 'key', width: '34%' },
|
||||
{ name: 'Value', accessor: 'value', width: '46%' },
|
||||
{ name: '', accessor: '', width: '20%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleHeaderDrag}>
|
||||
{headers && headers.length
|
||||
? headers.map((header) => {
|
||||
return (
|
||||
<tr key={header.uid}>
|
||||
<td>
|
||||
<tr key={header.uid} data-uid={header.uid}>
|
||||
<td className='flex relative'>
|
||||
<SingleLineEditor
|
||||
value={header.name}
|
||||
theme={storedTheme}
|
||||
@ -138,8 +149,8 @@ const RequestHeaders = ({ item, collection }) => {
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</ReorderTable>
|
||||
</Table>
|
||||
<button className="btn-add-header text-link pr-2 py-3 mt-2 select-none" onClick={addHeader}>
|
||||
+ Add Header
|
||||
</button>
|
||||
|
@ -19,16 +19,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-add-var {
|
||||
font-size: 0.8125rem;
|
||||
@ -38,7 +30,8 @@ const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
border: solid 1px transparent;
|
||||
outline: none !important;
|
||||
background-color: inherit;
|
||||
color: ${(props) => props.theme.table.input.color};
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { addVar, updateVar, deleteVar } from 'providers/ReduxStore/slices/collections';
|
||||
import { addVar, updateVar, deleteVar, moveVar } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import InfoTip from 'components/InfoTip';
|
||||
@ -10,6 +10,8 @@ import StyledWrapper from './StyledWrapper';
|
||||
import toast from 'react-hot-toast';
|
||||
import { variableNameRegex } from 'utils/common/regex';
|
||||
import RemoveButton from 'components/RemoveButton/index';
|
||||
import Table from 'components/Table/index';
|
||||
import ReorderTable from 'components/ReorderTable/index';
|
||||
|
||||
const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
const dispatch = useDispatch();
|
||||
@ -73,35 +75,41 @@ const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleVarDrag = ({ updateReorderedItem }) => {
|
||||
dispatch(
|
||||
moveVar({
|
||||
type: varType,
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
updateReorderedItem
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
{varType === 'request' ? (
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<span>Value</span>
|
||||
</div>
|
||||
</td>
|
||||
) : (
|
||||
<td>
|
||||
<div className="flex items-center">
|
||||
<span>Expr</span>
|
||||
<InfoTip text="You can write any valid JS expression here" infotipId="response-var" />
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{vars && vars.length
|
||||
<Table
|
||||
headers={[
|
||||
{ name: 'Name', accessor: 'name', width: '40%' },
|
||||
{ name: varType === 'request' ? (
|
||||
<div className="flex items-center">
|
||||
<span>Value</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center">
|
||||
<span>Expr</span>
|
||||
<InfoTip text="You can write any valid JS expression here" infotipId="response-var" />
|
||||
</div>
|
||||
), accessor: 'value', width: '46%' },
|
||||
{ name: '', accessor: '', width: '14%' }
|
||||
]}
|
||||
>
|
||||
<ReorderTable updateReorderedItem={handleVarDrag}>
|
||||
{vars && vars.length
|
||||
? vars.map((_var) => {
|
||||
return (
|
||||
<tr key={_var.uid}>
|
||||
<td>
|
||||
<tr key={_var.uid} data-uid={_var.uid}>
|
||||
<td className='flex relative'>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
@ -150,8 +158,8 @@ const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</tbody>
|
||||
</table>
|
||||
</ReorderTable>
|
||||
</Table>
|
||||
<button className="btn-add-var text-link pr-2 py-3 mt-2 select-none" onClick={handleAddVar}>
|
||||
+ Add
|
||||
</button>
|
||||
|
@ -152,6 +152,8 @@ const Collection = ({ collection, searchText }) => {
|
||||
<div className="flex py-1 collection-name items-center" ref={drop}>
|
||||
<div
|
||||
className="flex flex-grow items-center overflow-hidden"
|
||||
onClick={handleCollapseCollection}
|
||||
onContextMenu={handleRightClick}
|
||||
>
|
||||
<IconChevronRight
|
||||
size={16}
|
||||
@ -160,9 +162,7 @@ const Collection = ({ collection, searchText }) => {
|
||||
style={{ width: 16, minWidth: 16, color: 'rgb(160 160 160)' }}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
<div className="ml-1" id="sidebar-collection-name"
|
||||
onClick={handleCollapseCollection}
|
||||
onContextMenu={handleRightClick}>
|
||||
<div className="ml-1" id="sidebar-collection-name">
|
||||
{collection.name}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -68,7 +68,7 @@ const ImportCollection = ({ onClose, handleSubmit }) => {
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Modal size="sm" title="Import Collection" hideFooter={true} handleConfirm={onClose} handleCancel={onClose}>
|
||||
<Modal size="sm" title="Import Collection" hideFooter={true} handleCancel={onClose}>
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-sm">Select the type of your existing collection :</h3>
|
||||
<div className="mt-4 grid grid-rows-2 grid-flow-col gap-2">
|
||||
|
@ -15,7 +15,7 @@ const KeyValueExplorer = ({ data = [], theme }) => {
|
||||
<SecretToggle showSecret={showSecret} onClick={() => setShowSecret(!showSecret)} />
|
||||
<table className="border-collapse">
|
||||
<tbody>
|
||||
{data.map((envVar) => (
|
||||
{data.toSorted((a, b) => a.name.localeCompare(b.name)).map((envVar) => (
|
||||
<tr key={envVar.name}>
|
||||
<td className="px-2 py-1">{envVar.name}</td>
|
||||
<td className="px-2 py-1">
|
||||
|
@ -58,7 +58,7 @@ const trackStart = () => {
|
||||
event: 'start',
|
||||
properties: {
|
||||
os: platformLib.os.family,
|
||||
version: '1.36.0'
|
||||
version: '1.38.1'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -473,6 +473,10 @@ export const collectionsSlice = createSlice({
|
||||
item.draft.request.auth.mode = 'digest';
|
||||
item.draft.request.auth.digest = action.payload.content;
|
||||
break;
|
||||
case 'ntlm':
|
||||
item.draft.request.auth.mode = 'ntlm';
|
||||
item.draft.request.auth.ntlm = action.payload.content;
|
||||
break;
|
||||
case 'oauth2':
|
||||
item.draft.request.auth.mode = 'oauth2';
|
||||
item.draft.request.auth.oauth2 = action.payload.content;
|
||||
@ -528,11 +532,16 @@ export const collectionsSlice = createSlice({
|
||||
const { updateReorderedItem } = action.payload;
|
||||
const params = item.draft.request.params;
|
||||
|
||||
item.draft.request.params = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
const queryParams = params.filter((param) => param.type === 'query');
|
||||
const pathParams = params.filter((param) => param.type === 'path');
|
||||
|
||||
// Reorder only query params based on updateReorderedItem
|
||||
const reorderedQueryParams = updateReorderedItem.map((uid) => {
|
||||
return queryParams.find((param) => param.uid === uid);
|
||||
});
|
||||
|
||||
// update request url
|
||||
item.draft.request.params = [...reorderedQueryParams, ...pathParams];
|
||||
|
||||
// Update request URL
|
||||
const parts = splitOnFirst(item.draft.request.url, '?');
|
||||
const query = stringifyQueryParams(filter(item.draft.request.params, (p) => p.enabled && p.type === 'query'));
|
||||
if (query && query.length) {
|
||||
@ -690,6 +699,28 @@ export const collectionsSlice = createSlice({
|
||||
}
|
||||
}
|
||||
},
|
||||
moveRequestHeader: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
if (collection) {
|
||||
const item = findItemInCollection(collection, action.payload.itemUid);
|
||||
|
||||
if (item && isItemARequest(item)) {
|
||||
// Ensure item.draft is a deep clone of item if not already present
|
||||
if (!item.draft) {
|
||||
item.draft = cloneDeep(item);
|
||||
}
|
||||
|
||||
// Extract payload data
|
||||
const { updateReorderedItem } = action.payload;
|
||||
const params = item.draft.request.headers;
|
||||
|
||||
item.draft.request.headers = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
addFormUrlEncodedParam: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
@ -748,6 +779,28 @@ export const collectionsSlice = createSlice({
|
||||
}
|
||||
}
|
||||
},
|
||||
moveFormUrlEncodedParam: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
if (collection) {
|
||||
const item = findItemInCollection(collection, action.payload.itemUid);
|
||||
|
||||
if (item && isItemARequest(item)) {
|
||||
// Ensure item.draft is a deep clone of item if not already present
|
||||
if (!item.draft) {
|
||||
item.draft = cloneDeep(item);
|
||||
}
|
||||
|
||||
// Extract payload data
|
||||
const { updateReorderedItem } = action.payload;
|
||||
const params = item.draft.request.body.formUrlEncoded;
|
||||
|
||||
item.draft.request.body.formUrlEncoded = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
addMultipartFormParam: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
@ -810,6 +863,28 @@ export const collectionsSlice = createSlice({
|
||||
}
|
||||
}
|
||||
},
|
||||
moveMultipartFormParam: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
if (collection) {
|
||||
const item = findItemInCollection(collection, action.payload.itemUid);
|
||||
|
||||
if (item && isItemARequest(item)) {
|
||||
// Ensure item.draft is a deep clone of item if not already present
|
||||
if (!item.draft) {
|
||||
item.draft = cloneDeep(item);
|
||||
}
|
||||
|
||||
// Extract payload data
|
||||
const { updateReorderedItem } = action.payload;
|
||||
const params = item.draft.request.body.multipartForm;
|
||||
|
||||
item.draft.request.body.multipartForm = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
updateRequestAuthMode: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
@ -1023,6 +1098,28 @@ export const collectionsSlice = createSlice({
|
||||
}
|
||||
}
|
||||
},
|
||||
moveAssertion: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
if (collection) {
|
||||
const item = findItemInCollection(collection, action.payload.itemUid);
|
||||
|
||||
if (item && isItemARequest(item)) {
|
||||
// Ensure item.draft is a deep clone of item if not already present
|
||||
if (!item.draft) {
|
||||
item.draft = cloneDeep(item);
|
||||
}
|
||||
|
||||
// Extract payload data
|
||||
const { updateReorderedItem } = action.payload;
|
||||
const params = item.draft.request.assertions;
|
||||
|
||||
item.draft.request.assertions = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
addVar: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
const type = action.payload.type;
|
||||
@ -1117,6 +1214,37 @@ export const collectionsSlice = createSlice({
|
||||
}
|
||||
}
|
||||
},
|
||||
moveVar: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
const type = action.payload.type;
|
||||
|
||||
if (collection) {
|
||||
const item = findItemInCollection(collection, action.payload.itemUid);
|
||||
|
||||
if (item && isItemARequest(item)) {
|
||||
// Ensure item.draft is a deep clone of item if not already present
|
||||
if (!item.draft) {
|
||||
item.draft = cloneDeep(item);
|
||||
}
|
||||
|
||||
// Extract payload data
|
||||
const { updateReorderedItem } = action.payload;
|
||||
if(type == "request"){
|
||||
const params = item.draft.request.vars.req;
|
||||
|
||||
item.draft.request.vars.req = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
} else if (type === 'response') {
|
||||
const params = item.draft.request.vars.res;
|
||||
|
||||
item.draft.request.vars.res = updateReorderedItem.map((uid) => {
|
||||
return params.find((param) => param.uid === uid);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateCollectionAuthMode: (state, action) => {
|
||||
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||
|
||||
@ -1144,6 +1272,9 @@ export const collectionsSlice = createSlice({
|
||||
case 'digest':
|
||||
set(collection, 'root.request.auth.digest', action.payload.content);
|
||||
break;
|
||||
case 'ntlm':
|
||||
set(collection, 'root.request.auth.ntlm', action.payload.content);
|
||||
break;
|
||||
case 'oauth2':
|
||||
set(collection, 'root.request.auth.oauth2', action.payload.content);
|
||||
break;
|
||||
@ -1794,12 +1925,15 @@ export const {
|
||||
addRequestHeader,
|
||||
updateRequestHeader,
|
||||
deleteRequestHeader,
|
||||
moveRequestHeader,
|
||||
addFormUrlEncodedParam,
|
||||
updateFormUrlEncodedParam,
|
||||
deleteFormUrlEncodedParam,
|
||||
moveFormUrlEncodedParam,
|
||||
addMultipartFormParam,
|
||||
updateMultipartFormParam,
|
||||
deleteMultipartFormParam,
|
||||
moveMultipartFormParam,
|
||||
updateRequestAuthMode,
|
||||
updateRequestBodyMode,
|
||||
updateRequestBody,
|
||||
@ -1812,9 +1946,11 @@ export const {
|
||||
addAssertion,
|
||||
updateAssertion,
|
||||
deleteAssertion,
|
||||
moveAssertion,
|
||||
addVar,
|
||||
updateVar,
|
||||
deleteVar,
|
||||
moveVar,
|
||||
addFolderHeader,
|
||||
updateFolderHeader,
|
||||
deleteFolderHeader,
|
||||
|
@ -340,6 +340,13 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
|
||||
password: get(si.request, 'auth.digest.password', '')
|
||||
};
|
||||
break;
|
||||
case 'ntlm':
|
||||
di.request.auth.ntlm = {
|
||||
username: get(si.request, 'auth.ntlm.username', ''),
|
||||
password: get(si.request, 'auth.ntlm.password', ''),
|
||||
domain: get(si.request, 'auth.ntlm.domain', '')
|
||||
};
|
||||
break;
|
||||
case 'oauth2':
|
||||
let grantType = get(si.request, 'auth.oauth2.grantType', '');
|
||||
switch (grantType) {
|
||||
@ -680,6 +687,10 @@ export const humanizeRequestAuthMode = (mode) => {
|
||||
label = 'Digest Auth';
|
||||
break;
|
||||
}
|
||||
case 'ntlm': {
|
||||
label = 'NTLM';
|
||||
break;
|
||||
}
|
||||
case 'oauth2': {
|
||||
label = 'OAuth 2.0';
|
||||
break;
|
||||
|
@ -50,8 +50,10 @@
|
||||
"@usebruno/common": "0.1.0",
|
||||
"@usebruno/js": "0.12.0",
|
||||
"@usebruno/lang": "0.12.0",
|
||||
"@usebruno/vm2": "^3.9.13",
|
||||
"aws4-axios": "^3.3.0",
|
||||
"axios": "1.7.5",
|
||||
"axios-ntlm": "^1.4.2",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
"decomment": "^0.9.5",
|
||||
|
@ -165,6 +165,13 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
request.awsv4config.profileName = _interpolate(request.awsv4config.profileName) || '';
|
||||
}
|
||||
|
||||
// interpolate vars for ntlmConfig auth
|
||||
if (request.ntlmConfig) {
|
||||
request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || '';
|
||||
request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || '';
|
||||
request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || '';
|
||||
}
|
||||
|
||||
if (request) return request;
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,7 @@ const prepareRequest = (item = {}, collection = {}) => {
|
||||
};
|
||||
|
||||
const collectionAuth = get(collection, 'root.request.auth');
|
||||
if (collectionAuth && request.auth.mode === 'inherit') {
|
||||
if (collectionAuth && request.auth?.mode === 'inherit') {
|
||||
if (collectionAuth.mode === 'basic') {
|
||||
axiosRequest.auth = {
|
||||
username: get(collectionAuth, 'basic.username'),
|
||||
@ -47,9 +47,27 @@ const prepareRequest = (item = {}, collection = {}) => {
|
||||
if (collectionAuth.mode === 'bearer') {
|
||||
axiosRequest.headers['Authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
|
||||
}
|
||||
|
||||
if (collectionAuth.mode === 'apikey') {
|
||||
if (collectionAuth.apikey?.placement === 'header') {
|
||||
axiosRequest.headers[collectionAuth.apikey?.key] = collectionAuth.apikey?.value;
|
||||
}
|
||||
|
||||
if (collectionAuth.apikey?.placement === 'queryparams') {
|
||||
if (axiosRequest.url && collectionAuth.apikey?.key) {
|
||||
try {
|
||||
const urlObj = new URL(request.url);
|
||||
urlObj.searchParams.set(collectionAuth.apikey?.key, collectionAuth.apikey?.value);
|
||||
axiosRequest.url = urlObj.toString();
|
||||
} catch (error) {
|
||||
console.error('Invalid URL:', request.url, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.auth) {
|
||||
if (request.auth && request.auth.mode !== 'inherit') {
|
||||
if (request.auth.mode === 'basic') {
|
||||
axiosRequest.auth = {
|
||||
username: get(request, 'auth.basic.username'),
|
||||
@ -68,6 +86,14 @@ const prepareRequest = (item = {}, collection = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (request.auth.mode === 'ntlm') {
|
||||
axiosRequest.ntlmConfig = {
|
||||
username: get(request, 'auth.ntlm.username'),
|
||||
password: get(request, 'auth.ntlm.password'),
|
||||
domain: get(request, 'auth.ntlm.domain')
|
||||
};
|
||||
}
|
||||
|
||||
if (request.auth.mode === 'bearer') {
|
||||
axiosRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ const { parseDataFromResponse } = require('../utils/common');
|
||||
const { getCookieStringForUrl, saveCookies, shouldUseCookies } = require('../utils/cookies');
|
||||
const { createFormData } = require('../utils/form-data');
|
||||
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
|
||||
const { NtlmClient } = require('axios-ntlm');
|
||||
|
||||
|
||||
const onConsoleLog = (type, args) => {
|
||||
console[type](...args);
|
||||
@ -250,8 +252,13 @@ const runSingleRequest = async function (
|
||||
|
||||
let response, responseTime;
|
||||
try {
|
||||
// run request
|
||||
const axiosInstance = makeAxiosInstance();
|
||||
|
||||
let axiosInstance = makeAxiosInstance();
|
||||
if (request.ntlmConfig) {
|
||||
axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance.defaults)
|
||||
delete request.ntlmConfig;
|
||||
}
|
||||
|
||||
|
||||
if (request.awsv4config) {
|
||||
// todo: make this happen in prepare-request.js
|
||||
|
@ -1,5 +1,4 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
|
||||
const { describe, it, expect, beforeEach } = require('@jest/globals');
|
||||
const prepareRequest = require('../../src/runner/prepare-request');
|
||||
|
||||
describe('prepare-request: prepareRequest', () => {
|
||||
@ -22,4 +21,144 @@ describe('prepare-request: prepareRequest', () => {
|
||||
expect(result.data).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Properly maps inherited auth from collectionRoot', () => {
|
||||
// Initialize Test Fixtures
|
||||
let collection, item;
|
||||
|
||||
beforeEach(() => {
|
||||
collection = {
|
||||
name: 'Test Collection',
|
||||
root: {
|
||||
request: {
|
||||
auth: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
item = {
|
||||
name: 'Test Request',
|
||||
type: 'http-request',
|
||||
request: {
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [],
|
||||
url: 'https://usebruno.com',
|
||||
auth: {
|
||||
mode: 'inherit'
|
||||
},
|
||||
script: {
|
||||
req: 'console.log("Pre Request")',
|
||||
res: 'console.log("Post Response")'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('API Key Authentication', () => {
|
||||
it('If collection auth is apikey in header', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: "apikey",
|
||||
apikey: {
|
||||
key: "x-api-key",
|
||||
value: "{{apiKey}}",
|
||||
placement: "header"
|
||||
}
|
||||
};
|
||||
|
||||
const result = prepareRequest(item, collection);
|
||||
expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}');
|
||||
});
|
||||
|
||||
it('If collection auth is apikey in header and request has existing headers', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: "apikey",
|
||||
apikey: {
|
||||
key: "x-api-key",
|
||||
value: "{{apiKey}}",
|
||||
placement: "header"
|
||||
}
|
||||
};
|
||||
|
||||
item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true });
|
||||
const result = prepareRequest(item, collection);
|
||||
expect(result.headers).toHaveProperty('Content-Type', 'application/json');
|
||||
expect(result.headers).toHaveProperty('x-api-key', '{{apiKey}}');
|
||||
});
|
||||
|
||||
it('If collection auth is apikey in query parameters', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: "apikey",
|
||||
apikey: {
|
||||
key: "x-api-key",
|
||||
value: "{{apiKey}}",
|
||||
placement: "queryparams"
|
||||
}
|
||||
};
|
||||
|
||||
const urlObj = new URL(item.request.url);
|
||||
urlObj.searchParams.set(collection.root.request.auth.apikey.key, collection.root.request.auth.apikey.value);
|
||||
|
||||
const expected = urlObj.toString();
|
||||
const result = prepareRequest(item, collection);
|
||||
expect(result.url).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic Authentication', () => {
|
||||
it('If collection auth is basic auth', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: 'basic',
|
||||
basic: {
|
||||
username: 'testUser',
|
||||
password: 'testPass123'
|
||||
}
|
||||
};
|
||||
|
||||
const result = prepareRequest(item, collection);
|
||||
const expected = { username: 'testUser', password: 'testPass123' };
|
||||
expect(result.auth).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bearer Token Authentication', () => {
|
||||
it('If collection auth is bearer token', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: 'bearer',
|
||||
bearer: {
|
||||
token: 'token'
|
||||
}
|
||||
};
|
||||
|
||||
const result = prepareRequest(item, collection);
|
||||
expect(result.headers).toHaveProperty('Authorization', 'Bearer token');
|
||||
});
|
||||
|
||||
it('If collection auth is bearer token and request has existing headers', () => {
|
||||
collection.root.request.auth = {
|
||||
mode: 'bearer',
|
||||
bearer: {
|
||||
token: 'token'
|
||||
}
|
||||
};
|
||||
|
||||
item.request.headers.push({ name: 'Content-Type', value: 'application/json', enabled: true });
|
||||
|
||||
const result = prepareRequest(item, collection);
|
||||
expect(result.headers).toHaveProperty('Authorization', 'Bearer token');
|
||||
expect(result.headers).toHaveProperty('Content-Type', 'application/json');
|
||||
});
|
||||
});
|
||||
|
||||
describe('No Authentication', () => {
|
||||
it('If request does not have auth configured', () => {
|
||||
delete item.request.auth;
|
||||
let result;
|
||||
expect(() => {
|
||||
result = prepareRequest(item, collection);
|
||||
}).not.toThrow();
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "v1.36.0",
|
||||
"version": "v1.38.1",
|
||||
"name": "bruno",
|
||||
"description": "Opensource API Client for Exploring and Testing APIs",
|
||||
"homepage": "https://www.usebruno.com",
|
||||
@ -19,7 +19,9 @@
|
||||
"test": "node --experimental-vm-modules $(npx which jest)"
|
||||
},
|
||||
"jest": {
|
||||
"modulePaths": ["node_modules"]
|
||||
"modulePaths": [
|
||||
"node_modules"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-providers": "3.658.1",
|
||||
@ -28,9 +30,11 @@
|
||||
"@usebruno/lang": "0.12.0",
|
||||
"@usebruno/node-machine-id": "^2.0.0",
|
||||
"@usebruno/schema": "0.7.0",
|
||||
"@usebruno/vm2": "^3.9.13",
|
||||
"about-window": "^1.15.2",
|
||||
"aws4-axios": "^3.3.0",
|
||||
"axios": "1.7.5",
|
||||
"axios-ntlm": "^1.4.2",
|
||||
"chai": "^4.3.7",
|
||||
"chokidar": "^3.5.3",
|
||||
"content-disposition": "^0.5.4",
|
||||
@ -55,7 +59,6 @@
|
||||
"socks-proxy-agent": "^8.0.2",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"uuid": "^9.0.0",
|
||||
"@usebruno/vm2": "^3.9.13",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
@ -40,6 +40,7 @@ const iconv = require('iconv-lite');
|
||||
const FormData = require('form-data');
|
||||
const { createFormData } = require('../../utils/form-data');
|
||||
const { findItemInCollectionByPathname } = require('../../utils/collection');
|
||||
const { NtlmClient } = require('axios-ntlm');
|
||||
|
||||
const safeStringifyJSON = (data) => {
|
||||
try {
|
||||
@ -272,7 +273,15 @@ const configureRequest = async (
|
||||
...httpsAgentRequestFields
|
||||
});
|
||||
}
|
||||
const axiosInstance = makeAxiosInstance();
|
||||
|
||||
|
||||
let axiosInstance = makeAxiosInstance();
|
||||
|
||||
if (request.ntlmConfig) {
|
||||
axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance.defaults)
|
||||
delete request.ntlmConfig;
|
||||
}
|
||||
|
||||
|
||||
if (request.oauth2) {
|
||||
let requestCopy = cloneDeep(request);
|
||||
|
@ -231,6 +231,14 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
request.wsse.password = _interpolate(request.wsse.password) || '';
|
||||
}
|
||||
|
||||
|
||||
// interpolate vars for ntlmConfig auth
|
||||
if (request.ntlmConfig) {
|
||||
request.ntlmConfig.username = _interpolate(request.ntlmConfig.username) || '';
|
||||
request.ntlmConfig.password = _interpolate(request.ntlmConfig.password) || '';
|
||||
request.ntlmConfig.domain = _interpolate(request.ntlmConfig.domain) || '';
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ const { getTreePathFromCollectionToItem, mergeHeaders, mergeScripts, mergeVars }
|
||||
const { buildFormUrlEncodedPayload, createFormData } = require('../../utils/form-data');
|
||||
|
||||
const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
|
||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||
if (collectionAuth && request.auth.mode === 'inherit') {
|
||||
switch (collectionAuth.mode) {
|
||||
@ -33,6 +34,13 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
password: get(collectionAuth, 'digest.password')
|
||||
};
|
||||
break;
|
||||
case 'ntlm':
|
||||
axiosRequest.ntlmConfig = {
|
||||
username: get(collectionAuth, 'ntlm.username'),
|
||||
password: get(collectionAuth, 'ntlm.password'),
|
||||
domain: get(collectionAuth, 'ntlm.domain')
|
||||
};
|
||||
break;
|
||||
case 'wsse':
|
||||
const username = get(request, 'auth.wsse.username', '');
|
||||
const password = get(request, 'auth.wsse.password', '');
|
||||
@ -89,6 +97,13 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
password: get(request, 'auth.digest.password')
|
||||
};
|
||||
break;
|
||||
case 'ntlm':
|
||||
axiosRequest.ntlmConfig = {
|
||||
username: get(request, 'auth.ntlm.username'),
|
||||
password: get(request, 'auth.ntlm.password'),
|
||||
domain: get(request, 'auth.ntlm.domain')
|
||||
};
|
||||
break;
|
||||
case 'oauth2':
|
||||
const grantType = get(request, 'auth.oauth2.grantType');
|
||||
switch (grantType) {
|
||||
|
@ -22,12 +22,6 @@ const toNumber = (value) => {
|
||||
return Number.isInteger(num) ? parseInt(value, 10) : parseFloat(value);
|
||||
};
|
||||
|
||||
const removeQuotes = (str) => {
|
||||
if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'"))) {
|
||||
return str.slice(1, -1);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
const executeQuickJsVm = ({ script: externalScript, context: externalContext, scriptType = 'template-literal' }) => {
|
||||
if (!externalScript?.length || typeof externalScript !== 'string') {
|
||||
@ -44,7 +38,6 @@ const executeQuickJsVm = ({ script: externalScript, context: externalContext, sc
|
||||
if (externalScript === 'null') return null;
|
||||
if (externalScript === 'undefined') return undefined;
|
||||
|
||||
externalScript = removeQuotes(externalScript);
|
||||
|
||||
const vm = QuickJSSyncContext;
|
||||
|
||||
@ -94,7 +87,6 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
|
||||
if (externalScript === 'null') return null;
|
||||
if (externalScript === 'undefined') return undefined;
|
||||
|
||||
externalScript = removeQuotes(externalScript);
|
||||
|
||||
try {
|
||||
const module = await newQuickJSWASMModule();
|
||||
|
@ -85,14 +85,6 @@ const evaluateJsTemplateLiteral = (templateLiteral, context) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (templateLiteral.startsWith('"') && templateLiteral.endsWith('"')) {
|
||||
return templateLiteral.slice(1, -1);
|
||||
}
|
||||
|
||||
if (templateLiteral.startsWith("'") && templateLiteral.endsWith("'")) {
|
||||
return templateLiteral.slice(1, -1);
|
||||
}
|
||||
|
||||
if (!isNaN(templateLiteral)) {
|
||||
const number = Number(templateLiteral);
|
||||
// Check if the number is too high. Too high number might get altered, see #1000
|
||||
|
@ -23,7 +23,7 @@ const { outdentString } = require('../../v1/src/utils');
|
||||
*/
|
||||
const grammar = ohm.grammar(`Bru {
|
||||
BruFile = (meta | http | query | params | headers | auths | bodies | varsandassert | script | tests | docs)*
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authwsse | authapikey
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM | authOAuth2 | authwsse | authapikey
|
||||
bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body
|
||||
bodyforms = bodyformurlencoded | bodymultipart
|
||||
params = paramspath | paramsquery
|
||||
@ -87,6 +87,7 @@ const grammar = ohm.grammar(`Bru {
|
||||
authbasic = "auth:basic" dictionary
|
||||
authbearer = "auth:bearer" dictionary
|
||||
authdigest = "auth:digest" dictionary
|
||||
authNTLM = "auth:ntlm" dictionary
|
||||
authOAuth2 = "auth:oauth2" dictionary
|
||||
authwsse = "auth:wsse" dictionary
|
||||
authapikey = "auth:apikey" dictionary
|
||||
@ -450,6 +451,26 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
}
|
||||
};
|
||||
},
|
||||
authNTLM(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const usernameKey = _.find(auth, { name: 'username' });
|
||||
const passwordKey = _.find(auth, { name: 'password' });
|
||||
const domainKey = _.find(auth, { name: 'domain' });
|
||||
|
||||
const username = usernameKey ? usernameKey.value : '';
|
||||
const password = passwordKey ? passwordKey.value : '';
|
||||
const domain = passwordKey ? domainKey.value : '';
|
||||
|
||||
return {
|
||||
auth: {
|
||||
ntlm: {
|
||||
username,
|
||||
password,
|
||||
domain
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
authOAuth2(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const grantTypeKey = _.find(auth, { name: 'grant_type' });
|
||||
|
@ -4,7 +4,7 @@ const { outdentString } = require('../../v1/src/utils');
|
||||
|
||||
const grammar = ohm.grammar(`Bru {
|
||||
BruFile = (meta | query | headers | auth | auths | vars | script | tests | docs)*
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest | authOAuth2 | authwsse | authapikey
|
||||
auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM |authOAuth2 | authwsse | authapikey
|
||||
|
||||
nl = "\\r"? "\\n"
|
||||
st = " " | "\\t"
|
||||
@ -42,6 +42,7 @@ const grammar = ohm.grammar(`Bru {
|
||||
authbasic = "auth:basic" dictionary
|
||||
authbearer = "auth:bearer" dictionary
|
||||
authdigest = "auth:digest" dictionary
|
||||
authNTLM = "auth:ntlm" dictionary
|
||||
authOAuth2 = "auth:oauth2" dictionary
|
||||
authwsse = "auth:wsse" dictionary
|
||||
authapikey = "auth:apikey" dictionary
|
||||
@ -245,6 +246,26 @@ const sem = grammar.createSemantics().addAttribute('ast', {
|
||||
}
|
||||
};
|
||||
},
|
||||
authNTLM(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const usernameKey = _.find(auth, { name: 'username' });
|
||||
const passwordKey = _.find(auth, { name: 'password' });
|
||||
const domainKey = _.find(auth, { name: 'domain' });
|
||||
|
||||
const username = usernameKey ? usernameKey.value : '';
|
||||
const password = passwordKey ? passwordKey.value : '';
|
||||
const domain = domainKey ? domainKey.value : '';
|
||||
|
||||
return {
|
||||
auth: {
|
||||
ntlm: {
|
||||
username,
|
||||
password,
|
||||
domain
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
authOAuth2(_1, dictionary) {
|
||||
const auth = mapPairListToKeyValPairs(dictionary.ast, false);
|
||||
const grantTypeKey = _.find(auth, { name: 'grant_type' });
|
||||
|
@ -165,6 +165,18 @@ ${indentString(`password: ${auth?.digest?.password || ''}`)}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
if (auth && auth.ntlm) {
|
||||
bru += `auth:ntlm {
|
||||
${indentString(`username: ${auth?.ntlm?.username || ''}`)}
|
||||
${indentString(`password: ${auth?.ntlm?.password || ''}`)}
|
||||
${indentString(`domain: ${auth?.ntlm?.domain || ''}`)}
|
||||
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
if (auth && auth.oauth2) {
|
||||
switch (auth?.oauth2?.grantType) {
|
||||
case 'password':
|
||||
|
@ -120,6 +120,17 @@ ${indentString(`username: ${auth.digest.username}`)}
|
||||
${indentString(`password: ${auth.digest.password}`)}
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
if (auth && auth.ntlm) {
|
||||
bru += `auth:ntlm {
|
||||
${indentString(`username: ${auth.ntlm.username}`)}
|
||||
${indentString(`password: ${auth.ntlm.password}`)}
|
||||
${indentString(`domain: ${auth.ntlm.domain}`)}
|
||||
|
||||
}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -127,6 +127,17 @@ const authDigestSchema = Yup.object({
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
|
||||
|
||||
const authNTLMSchema = Yup.object({
|
||||
username: Yup.string().nullable(),
|
||||
password: Yup.string().nullable(),
|
||||
domain: Yup.string().nullable()
|
||||
|
||||
})
|
||||
.noUnknown(true)
|
||||
.strict();
|
||||
|
||||
const authApiKeySchema = Yup.object({
|
||||
key: Yup.string().nullable(),
|
||||
value: Yup.string().nullable(),
|
||||
@ -195,11 +206,12 @@ const oauth2Schema = Yup.object({
|
||||
|
||||
const authSchema = Yup.object({
|
||||
mode: Yup.string()
|
||||
.oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'oauth2', 'wsse', 'apikey'])
|
||||
.oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'ntlm', 'oauth2', 'wsse', 'apikey'])
|
||||
.required('mode is required'),
|
||||
awsv4: authAwsV4Schema.nullable(),
|
||||
basic: authBasicSchema.nullable(),
|
||||
bearer: authBearerSchema.nullable(),
|
||||
ntlm: authNTLMSchema.nullable(),
|
||||
digest: authDigestSchema.nullable(),
|
||||
oauth2: oauth2Schema.nullable(),
|
||||
wsse: authWsseSchema.nullable(),
|
||||
|
Loading…
Reference in New Issue
Block a user