diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/StyledWrapper.js new file mode 100644 index 000000000..6613dd1f3 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/StyledWrapper.js @@ -0,0 +1,25 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + font-size: 0.8125rem; + + .auth-mode-selector { + background: ${(props) => props.theme.requestTabPanel.bodyModeSelect.color}; + border-radius: 3px; + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + } + + .label-item { + padding: 0.2rem 0.6rem !important; + } + } + + .caret { + color: rgb(140, 140, 140); + fill: rgb(140 140 140); + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js new file mode 100644 index 000000000..3ad80ce13 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/AuthMode/index.js @@ -0,0 +1,70 @@ +import React, { useRef, forwardRef } from 'react'; +import get from 'lodash/get'; +import { IconCaretDown } from '@tabler/icons'; +import Dropdown from 'components/Dropdown'; +import { useDispatch } from 'react-redux'; +import { updateRequestAuthMode } from 'providers/ReduxStore/slices/collections'; +import { humanizeRequestAuthMode } from 'utils/collections'; +import StyledWrapper from './StyledWrapper'; + +const AuthMode = ({ item, collection }) => { + const dispatch = useDispatch(); + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); + + const Icon = forwardRef((props, ref) => { + return ( +
+ {humanizeRequestAuthMode(authMode)} +
+ ); + }); + + const onModeChange = (value) => { + dispatch( + updateRequestAuthMode({ + itemUid: item.uid, + collectionUid: collection.uid, + mode: value + }) + ); + }; + + return ( + +
+ } placement="bottom-end"> +
{ + dropdownTippyRef.current.hide(); + onModeChange('basic'); + }} + > + Basic Auth +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('bearer'); + }} + > + Bearer Token +
+
{ + dropdownTippyRef.current.hide(); + onModeChange('none'); + }} + > + No Auth +
+
+
+
+ ); +}; +export default AuthMode; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/StyledWrapper.js new file mode 100644 index 000000000..e49220854 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/StyledWrapper.js @@ -0,0 +1,5 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div``; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/index.js b/packages/bruno-app/src/components/RequestPane/Auth/index.js new file mode 100644 index 000000000..e2f345d23 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/index.js @@ -0,0 +1,32 @@ +import React from 'react'; +import get from 'lodash/get'; +import { useDispatch } from 'react-redux'; +import { updateRequestBody } from 'providers/ReduxStore/slices/collections'; +import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; + +const RequestBody = ({ item, collection }) => { + const dispatch = useDispatch(); + const authMode = item.draft ? get(item, 'draft.request.auth.mode') : get(item, 'request.auth.mode'); + + const onEdit = (value) => { + // dispatch( + // updateRequestBody({ + // content: value, + // itemUid: item.uid, + // collectionUid: collection.uid + // }) + // ); + }; + + if (authMode === 'basic') { + return
Basic Auth
; + } + + if (authMode === 'bearer') { + return
Bearer Token
; + } + + return No Auth; +}; +export default RequestBody; diff --git a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js index caace776f..652414a62 100644 --- a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js +++ b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js @@ -7,6 +7,8 @@ import QueryParams from 'components/RequestPane/QueryParams'; import RequestHeaders from 'components/RequestPane/RequestHeaders'; import RequestBody from 'components/RequestPane/RequestBody'; import RequestBodyMode from 'components/RequestPane/RequestBody/RequestBodyMode'; +import Auth from 'components/RequestPane/Auth'; +import AuthMode from 'components/RequestPane/Auth/AuthMode'; import Vars from 'components/RequestPane/Vars'; import Assertions from 'components/RequestPane/Assertions'; import Script from 'components/RequestPane/Script'; @@ -38,6 +40,9 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => { case 'headers': { return ; } + case 'auth': { + return ; + } case 'vars': { return ; } @@ -83,6 +88,9 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
selectTab('headers')}> Headers
+
selectTab('auth')}> + Auth +
selectTab('vars')}> Vars
@@ -95,13 +103,16 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
selectTab('tests')}> Tests
- {/* Moved to post mvp */} - {/*
selectTab('auth')}>Auth
*/} {focusedTab.requestPaneTab === 'body' ? (
) : null} + {focusedTab.requestPaneTab === 'auth' ? ( +
+ +
+ ) : null}
{getTabPanel(focusedTab.requestPaneTab)} diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index 495989eb1..aca21025a 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -563,6 +563,20 @@ export const collectionsSlice = createSlice({ } } }, + updateRequestAuthMode: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + + if (collection && collection.items && collection.items.length) { + const item = findItemInCollection(collection, action.payload.itemUid); + + if (item && isItemARequest(item)) { + if (!item.draft) { + item.draft = cloneDeep(item); + } + item.draft.request.auth.mode = action.payload.mode; + } + } + }, updateRequestBodyMode: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -1170,6 +1184,7 @@ export const { addMultipartFormParam, updateMultipartFormParam, deleteMultipartFormParam, + updateRequestAuthMode, updateRequestBodyMode, updateRequestBody, updateRequestGraphqlQuery, diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index 80fe41dd3..c982f4147 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -445,6 +445,22 @@ export const humanizeRequestBodyMode = (mode) => { return label; }; +export const humanizeRequestAuthMode = (mode) => { + let label = 'No Auth'; + switch (mode) { + case 'basic': { + label = 'Basic Auth'; + break; + } + case 'bearer': { + label = 'Bearer Token'; + break; + } + } + + return label; +}; + export const refreshUidsInItem = (item) => { item.uid = uuid(); diff --git a/packages/bruno-electron/src/bru/index.js b/packages/bruno-electron/src/bru/index.js index 45b10004f..a28c04a7b 100644 --- a/packages/bruno-electron/src/bru/index.js +++ b/packages/bruno-electron/src/bru/index.js @@ -61,6 +61,7 @@ const bruToJson = (bru) => { url: _.get(json, 'http.url'), params: _.get(json, 'query', []), headers: _.get(json, 'headers', []), + auth: _.get(json, 'auth', {}), body: _.get(json, 'body', {}), script: _.get(json, 'script', {}), vars: _.get(json, 'vars', {}), @@ -69,6 +70,7 @@ const bruToJson = (bru) => { } }; + transformedJson.request.auth.mode = _.get(json, 'http.auth', 'none'); transformedJson.request.body.mode = _.get(json, 'http.body', 'none'); return transformedJson; @@ -104,10 +106,12 @@ const jsonToBru = (json) => { http: { method: _.lowerCase(_.get(json, 'request.method')), url: _.get(json, 'request.url'), + auth: _.get(json, 'request.auth.mode', 'none'), body: _.get(json, 'request.body.mode', 'none') }, query: _.get(json, 'request.params', []), headers: _.get(json, 'request.headers', []), + auth: _.get(json, 'request.auth', {}), body: _.get(json, 'request.body', {}), script: _.get(json, 'request.script', {}), vars: { diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index 7076175b5..81cd2528b 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -69,6 +69,27 @@ const requestBodySchema = Yup.object({ .noUnknown(true) .strict(); +const authBasicSchema = Yup.object({ + username: Yup.string().nullable(), + password: Yup.string().nullable() +}) + .noUnknown(true) + .strict(); + +const authBearerSchema = Yup.object({ + token: Yup.string().nullable() +}) + .noUnknown(true) + .strict(); + +const authSchema = Yup.object({ + mode: Yup.string().oneOf(['none', 'basic', 'bearer']).required('mode is required'), + basic: authBasicSchema.nullable(), + bearer: authBearerSchema.nullable() +}) + .noUnknown(true) + .strict(); + // Right now, the request schema is very tightly coupled with http request // As we introduce more request types in the future, we will improve the definition to support // schema structure based on other request type @@ -77,6 +98,7 @@ const requestSchema = Yup.object({ method: requestMethodSchema, headers: Yup.array().of(keyValueSchema).required('headers are required'), params: Yup.array().of(keyValueSchema).required('params are required'), + auth: authSchema, body: requestBodySchema, script: Yup.object({ req: Yup.string().nullable(),