mirror of
https://github.com/chenasraf/redar-browser.git
synced 2026-05-18 01:59:00 +00:00
Extract request headers from Header component + cleanup
This commit is contained in:
@@ -34,11 +34,11 @@ export interface IAction<T = any> {
|
||||
|
||||
export const AppDispatcher = new Dispatcher<IAction>()
|
||||
|
||||
function innerObjFromKey<T = any>(obj: T, k: string): Immutable.OrderedMap<string, T> {
|
||||
return Immutable.OrderedMap<string, any>(obj || {}).get(k, {})
|
||||
function innerObjFromKey<T = any>(obj: T, k: string): Immutable.Map<string, T> {
|
||||
return Immutable.Map<string, any>(obj || {}).get(k, {})
|
||||
}
|
||||
|
||||
export type IState = Immutable.OrderedMap<string, any>
|
||||
export type IState = Immutable.Map<string, any>
|
||||
|
||||
class AppStore extends ReduceStore<IState, IAction> {
|
||||
constructor() {
|
||||
@@ -46,12 +46,13 @@ class AppStore extends ReduceStore<IState, IAction> {
|
||||
}
|
||||
|
||||
getInitialState() {
|
||||
const t = Immutable.OrderedMap<string, any>([
|
||||
return Immutable.Map<string, any>([
|
||||
[StoreKeys.ViewKey, localStorage.lastViewKey || ''],
|
||||
[StoreKeys.RequestType, localStorage.lastRequestType || 'JSON'],
|
||||
[StoreKeys.RequestPayload, localStorage.lastRequestPayload || ''],
|
||||
[StoreKeys.RequestPayload, localStorage.lastPayload || ''],
|
||||
[StoreKeys.RequestURL, localStorage.lastURL || ''],
|
||||
[StoreKeys.RequestHeaders, localStorage.lastHeaders || ''],
|
||||
])
|
||||
return t
|
||||
}
|
||||
|
||||
reduce(state: IState, action: IAction) {
|
||||
@@ -66,6 +67,18 @@ class AppStore extends ReduceStore<IState, IAction> {
|
||||
case ActionTypes.UPDATE_VIEWKEY:
|
||||
localStorage.lastViewKey = action.payload
|
||||
return state.set(StoreKeys.ViewKey, action.payload)
|
||||
case ActionTypes.UPDATE_REQ_HEADERS:
|
||||
localStorage.lastHeaders = action.payload
|
||||
return state.set(StoreKeys.RequestHeaders, action.payload)
|
||||
case ActionTypes.UPDATE_REQ_URL:
|
||||
localStorage.lastURL = action.payload
|
||||
return state.set(StoreKeys.RequestURL, action.payload)
|
||||
case ActionTypes.UPDATE_REQ_METHOD:
|
||||
localStorage.lastMethod = action.payload
|
||||
return state.set(StoreKeys.RequestMethod, action.payload)
|
||||
case ActionTypes.UPDATE_REQ_PAYLOAD:
|
||||
localStorage.lastPayload = action.payload
|
||||
return state.set(StoreKeys.RequestPayload, action.payload)
|
||||
case ActionTypes.SEND_REQUEST:
|
||||
axios.request(action.payload)
|
||||
.then((response: AxiosResponse) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export interface IProps {
|
||||
url?: string
|
||||
handleChange?(value: string, e: React.ChangeEvent<HTMLInputElement>): void
|
||||
store: any
|
||||
url: string
|
||||
onChange?(value: string, e: React.ChangeEvent<HTMLInputElement>): void
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import * as React from 'react'
|
||||
import * as css from './AddressBar.css'
|
||||
import * as I from './AddressBar.module'
|
||||
import Dispatcher from 'src/common/Dispatcher'
|
||||
import Dispatcher, { StoreKeys } from 'src/common/Dispatcher'
|
||||
|
||||
class AddressBar extends React.Component<I.IProps, I.IState> {
|
||||
constructor(props: I.IProps) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
url: props.url || ''
|
||||
url: props.url
|
||||
}
|
||||
}
|
||||
private handleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
private onChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
const { value } = e.target as HTMLInputElement
|
||||
if (typeof this.props.handleChange === 'function') {
|
||||
this.props.handleChange(value, e)
|
||||
}
|
||||
this.setState({ url: value }, () => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(value, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: I.IProps) {
|
||||
@@ -29,7 +31,7 @@ class AddressBar extends React.Component<I.IProps, I.IState> {
|
||||
<div className={css.addressBar}>
|
||||
<input placeholder="https://www.example.com/request"
|
||||
value={this.state.url}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.handleChange(e)}
|
||||
onChange={(e) => this.onChange(e)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,30 +14,4 @@
|
||||
grid-template-columns: [payload] 2fr [headers] 1fr;
|
||||
grid-column-gap: 14px;
|
||||
width: 100%;
|
||||
|
||||
& .title {
|
||||
text-transform: uppercase;
|
||||
color: #555;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.payload {
|
||||
grid-area: payload;
|
||||
}
|
||||
|
||||
.headers {
|
||||
grid-area: headers;
|
||||
}
|
||||
|
||||
.payload, .headers {
|
||||
& textarea {
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
overflow-x: hidden;
|
||||
min-height: 50px;
|
||||
font-family: "Lucida Grande", "Courier New", monospace, serif;
|
||||
}
|
||||
}
|
||||
|
||||
3
src/components/Header/Header.css.d.ts
vendored
3
src/components/Header/Header.css.d.ts
vendored
@@ -1,5 +1,2 @@
|
||||
export const header: string;
|
||||
export const requestDataContainer: string;
|
||||
export const title: string;
|
||||
export const payload: string;
|
||||
export const headers: string;
|
||||
|
||||
@@ -9,6 +9,7 @@ import axios, { AxiosResponse } from 'axios'
|
||||
import { dispatch, register, ActionTypes, StoreKeys } from 'common/Dispatcher'
|
||||
import * as classNames from 'classnames'
|
||||
import NavBar from 'components/NavBar/NavBar'
|
||||
import RequestHeaders from 'components/RequestHeaders/RequestHeaders'
|
||||
|
||||
class Header extends React.Component<I.IProps, I.IState> {
|
||||
constructor(props: I.IProps) {
|
||||
@@ -16,7 +17,7 @@ class Header extends React.Component<I.IProps, I.IState> {
|
||||
this.state = {
|
||||
url: localStorage.lastUrl || '',
|
||||
method: localStorage.lastMethod || 'GET',
|
||||
requestPayload: localStorage.lastRequestPayload || '',
|
||||
requestPayload: localStorage.lastPayload || '',
|
||||
headers: localStorage.lastHeaders || '',
|
||||
}
|
||||
}
|
||||
@@ -27,44 +28,13 @@ class Header extends React.Component<I.IProps, I.IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private changeURL(url: string) {
|
||||
this.setState({ url })
|
||||
localStorage.lastUrl = url
|
||||
}
|
||||
|
||||
private changeMethod(method: string) {
|
||||
this.setState({ method })
|
||||
localStorage.lastMethod = method
|
||||
}
|
||||
|
||||
private changeRequestPayload(requestPayload: string) {
|
||||
this.setState({ requestPayload })
|
||||
localStorage.lastRequestPayload = requestPayload
|
||||
}
|
||||
|
||||
private changeHeaders(headers: string) {
|
||||
this.setState({ headers })
|
||||
localStorage.lastHeaders = headers
|
||||
}
|
||||
|
||||
private get requestHeaders() {
|
||||
const headers = this.state.headers.split(`\n`).reduce((accu, cur) => {
|
||||
const [key, val] = cur.split(':').map(s => s.trim())
|
||||
if (key.length) {
|
||||
accu[key] = val
|
||||
}
|
||||
return accu
|
||||
}, {})
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
private go() {
|
||||
const { method, url: url } = this.state
|
||||
dispatch(ActionTypes.SEND_REQUEST, {
|
||||
method, url,
|
||||
data: this.state.requestPayload,
|
||||
headers: this.requestHeaders
|
||||
method: this.props.store.get(StoreKeys.RequestMethod),
|
||||
url: this.props.store.get(StoreKeys.RequestURL),
|
||||
data: this.props.store.get(StoreKeys.RequestPayload),
|
||||
headers: this.props.store.get(StoreKeys.RequestHeaders),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,16 +43,8 @@ class Header extends React.Component<I.IProps, I.IState> {
|
||||
<div className={classNames(css.header, this.props.className)}>
|
||||
<NavBar store={this.props.store} />
|
||||
<div className={css.requestDataContainer}>
|
||||
<RequestType className={css.payload}
|
||||
onChange={(payload) => this.setState({ requestPayload: payload })}
|
||||
store={this.props.store} />
|
||||
<div className={css.headers}>
|
||||
<h4 className={css.title}>Headers</h4>
|
||||
<textarea name="headers"
|
||||
value={this.state.headers}
|
||||
placeholder="Headers"
|
||||
onChange={(e) => this.changeHeaders(e.target.value)} />
|
||||
</div>
|
||||
<RequestType store={this.props.store} />
|
||||
<RequestHeaders store={this.props.store} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
4
src/components/NavBar/NavBar.module.d.ts
vendored
4
src/components/NavBar/NavBar.module.d.ts
vendored
@@ -7,6 +7,8 @@ export interface IProps {
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||
method: Methods
|
||||
url: string
|
||||
}
|
||||
|
||||
export type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as classNames from 'classnames'
|
||||
import SelectBox, { Option, styles as selectBoxStyle } from 'components/SelectBox/SelectBox'
|
||||
import AddressBar from 'components/AddressBar/AddressBar'
|
||||
import Button from 'components/Button/Button'
|
||||
import { StoreKeys } from 'common/Dispatcher'
|
||||
import { StoreKeys, dispatch, ActionTypes } from 'common/Dispatcher'
|
||||
|
||||
class NavBar extends React.Component<I.IProps, I.IState> {
|
||||
private httpMethods = [
|
||||
@@ -23,13 +23,19 @@ class NavBar extends React.Component<I.IProps, I.IState> {
|
||||
}
|
||||
}
|
||||
|
||||
private changeMethod(value: string) {
|
||||
private changeMethod(value: I.Methods) {
|
||||
dispatch(ActionTypes.UPDATE_REQ_METHOD, value)
|
||||
this.setState({ method: value })
|
||||
|
||||
if (this.props.onChangeMethod) {
|
||||
this.props.onChangeMethod(value)
|
||||
}
|
||||
}
|
||||
|
||||
private changeURL(value: string) {
|
||||
dispatch(ActionTypes.UPDATE_REQ_URL, value)
|
||||
this.setState({ url: value })
|
||||
|
||||
if (this.props.onChangeURL) {
|
||||
this.props.onChangeURL(value)
|
||||
}
|
||||
@@ -53,12 +59,13 @@ class NavBar extends React.Component<I.IProps, I.IState> {
|
||||
value={this.state.method}
|
||||
options={this.httpMethods}
|
||||
placeholder="METHOD"
|
||||
onChange={(value: Option<string>) => this.changeMethod(value.value!)}
|
||||
onChange={(value: Option<I.Methods>) => this.changeMethod(value.value!)}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.address}>
|
||||
<AddressBar url={this.state.url}
|
||||
handleChange={(value) => this.changeURL(value)}/>
|
||||
store={this.props.store}
|
||||
onChange={(value) => this.changeURL(value)}/>
|
||||
</div>
|
||||
<div className={css.go}>
|
||||
<Button onClick={() => this.go()}>Go</Button>
|
||||
|
||||
29
src/components/RequestHeaders/RequestHeaders.css
Normal file
29
src/components/RequestHeaders/RequestHeaders.css
Normal file
@@ -0,0 +1,29 @@
|
||||
@import "../../_variables.css";
|
||||
|
||||
.RequestHeaders {
|
||||
display: grid;
|
||||
grid-row-gap: 5px;
|
||||
|
||||
& input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 1px solid #ccc;
|
||||
font-family: "Lucida Grande", "Courier New", monospace, serif;
|
||||
padding: 6px 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
text-transform: uppercase;
|
||||
color: #555;
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.pair {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-column-gap: 3px;
|
||||
grid-template-columns: repeat(2, 50%);
|
||||
min-width: 50%;
|
||||
}
|
||||
4
src/components/RequestHeaders/RequestHeaders.css.d.ts
vendored
Normal file
4
src/components/RequestHeaders/RequestHeaders.css.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export const RequestHeaders: string;
|
||||
export const requestHeaders: string;
|
||||
export const title: string;
|
||||
export const pair: string;
|
||||
12
src/components/RequestHeaders/RequestHeaders.module.d.ts
vendored
Normal file
12
src/components/RequestHeaders/RequestHeaders.module.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as Immutable from 'immutable'
|
||||
|
||||
export interface IProps {
|
||||
className?: string
|
||||
store: any
|
||||
}
|
||||
|
||||
export type HeaderSet = Immutable.List<[string, string]>
|
||||
|
||||
export interface IState {
|
||||
headers: HeaderSet
|
||||
}
|
||||
118
src/components/RequestHeaders/RequestHeaders.tsx
Normal file
118
src/components/RequestHeaders/RequestHeaders.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import * as React from 'react'
|
||||
import * as css from './RequestHeaders.css'
|
||||
import * as I from './RequestHeaders.module'
|
||||
import * as classNames from 'classnames'
|
||||
import * as Immutable from 'immutable'
|
||||
import { StoreKeys, ActionTypes, dispatch } from 'common/Dispatcher'
|
||||
|
||||
class RequestHeaders extends React.Component<I.IProps, I.IState> {
|
||||
constructor(props: I.IProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
headers: this.parseStringHeaders(props.store.get(StoreKeys.RequestHeaders, ''))
|
||||
}
|
||||
}
|
||||
|
||||
private parseStringHeaders(headers: string) {
|
||||
let headerMap = Immutable.List<[string, string]>()
|
||||
|
||||
headers.split('\n').forEach((header, idx) => {
|
||||
const [ name, value ] = header.split(':').map(s => s.trim())
|
||||
const finalName = name.split('-')
|
||||
.map(s => this.capitalize(s))
|
||||
.join('-')
|
||||
|
||||
if (finalName) {
|
||||
headerMap = headerMap.set(idx, [finalName, value || ''])
|
||||
}
|
||||
})
|
||||
|
||||
return headerMap
|
||||
}
|
||||
|
||||
private get stringHeaders() {
|
||||
const seq = this.state.headers.entrySeq()
|
||||
|
||||
return seq
|
||||
.map((header) => {
|
||||
if (!header) {
|
||||
return
|
||||
}
|
||||
let [ name, value ] = header[1]
|
||||
name = name.split('-').map(s => this.capitalize(s)).join('-')
|
||||
return name ?
|
||||
`${name}: ${value}`
|
||||
: undefined
|
||||
})
|
||||
.filter(s => Boolean(s))
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
private capitalize(str: string) {
|
||||
if (!str) {
|
||||
return ''
|
||||
}
|
||||
return str[0].toUpperCase() + str.slice(1).toLowerCase()
|
||||
}
|
||||
|
||||
private updateHeaderName(idx: number, name: string) {
|
||||
const value = this.state.headers.get(idx, [name, ''])
|
||||
this.setState({
|
||||
headers: this.state.headers.set(idx, [name, value[1]])
|
||||
}, () => {
|
||||
dispatch(ActionTypes.UPDATE_REQ_HEADERS, this.stringHeaders)
|
||||
})
|
||||
}
|
||||
|
||||
private updateHeaderValue(idx: number, value: string) {
|
||||
const header = this.state.headers.get(idx, ['', ''])
|
||||
|
||||
this.setState({
|
||||
headers: this.state.headers.set(idx, [header[0], value || ''])
|
||||
}, () => {
|
||||
dispatch(ActionTypes.UPDATE_REQ_HEADERS, this.stringHeaders)
|
||||
})
|
||||
}
|
||||
|
||||
private getHeaderName(idx: number) {
|
||||
return this.state.headers.get(idx, ['', ''])[0]
|
||||
}
|
||||
|
||||
private getHeaderValue(idx: number) {
|
||||
return this.state.headers.get(idx, ['', ''])[1]
|
||||
}
|
||||
|
||||
render() {
|
||||
const className = classNames(css.RequestHeaders, this.props.className)
|
||||
const inputRowRange = Immutable.Range(0, this.state.headers.count() + 1)
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<h4 className={css.title}>Headers</h4>
|
||||
{inputRowRange.map((i: number) => {
|
||||
return (
|
||||
<div key={`header-${i}`}
|
||||
className={css.pair}>
|
||||
<div>
|
||||
<input type="text"
|
||||
placeholder="Name"
|
||||
value={this.getHeaderName(i)}
|
||||
onChange={(e) => this.updateHeaderName(i, (e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text"
|
||||
placeholder="Value"
|
||||
value={this.getHeaderValue(i)}
|
||||
onChange={(e) => this.updateHeaderValue(i, (e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default RequestHeaders
|
||||
@@ -14,6 +14,7 @@
|
||||
text-transform: uppercase;
|
||||
color: $text-light;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +44,14 @@
|
||||
padding: 5px 7px;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
vertical-align: middle;
|
||||
height: 1rem;
|
||||
line-height: 1rem;
|
||||
opacity: 0.5;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,5 @@ export const cell: string;
|
||||
export const rowStart: string;
|
||||
export const rowEnd: string;
|
||||
export const filterInput: string;
|
||||
export const sortIcon: string;
|
||||
export const active: string;
|
||||
|
||||
@@ -52,10 +52,17 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
|
||||
private columns() {
|
||||
const keys = Object.keys(this.state.response[0] || {})
|
||||
return keys.map((key) => {
|
||||
const cls = classNames(css.sortIcon, 'material-icons', {
|
||||
[css.active]: this.state.sortKey === key
|
||||
})
|
||||
|
||||
return (
|
||||
<h3 key={`col-th-${key}`}
|
||||
onClick={() => this.sortBy(key)}>
|
||||
{key}
|
||||
<i className={cls}>
|
||||
{this.state.sortKey === key && this.state.sortDesc ? 'arrow_drop_down' : 'arrow_drop_up'}
|
||||
</i>
|
||||
</h3>
|
||||
)
|
||||
})
|
||||
@@ -163,7 +170,7 @@ class ResponseRepr extends React.Component<I.IProps, I.IState> {
|
||||
|
||||
return (
|
||||
<div className={css.table}
|
||||
style={{gridTemplateRows: `repeat(${colAmt}, auto)`}}>}
|
||||
style={{gridTemplateRows: `repeat(${colAmt}, auto)`}}>
|
||||
<RObject data={response} />
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user