Skip to content

Commit

Permalink
Added basic grid for inspector (#2)
Browse files Browse the repository at this point in the history
* Added basic grid for inspector

* Got vertical sizing to work for header tabs

* Cleaned up headers tab

* Parameters tab

* Added body tab

* Refactoring app.tsx into smaller components

* Cleaning up styles for Sanitizer component

* Fix build
  • Loading branch information
ehamai authored Dec 12, 2023
1 parent d1c8250 commit 1167da2
Show file tree
Hide file tree
Showing 16 changed files with 680 additions and 139 deletions.
35 changes: 0 additions & 35 deletions src/App.styles.ts

This file was deleted.

110 changes: 20 additions & 90 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,33 @@
import { FormEvent, useEffect, useState } from 'react';
import { SanitizationCategories, } from './sanitizer/sanitizer';
import { Link, Checkbox, Stack, Text } from '@fluentui/react';
import { onFileUpload } from './common/fileUpload';
import { checkboxStyle, containerStyle, fileUploadStyle, layoutStackStyle, logIssueLinkeStyle, radioButtonStackStyle } from './App.styles';

const stackTokens = {
childrenGap: 10
};
import { useEffect, useState } from 'react';
import { Link, Stack } from '@fluentui/react';
import { TraceInspector } from './components/TraceInspector/TraceInspector';
import { HarFile } from './sanitizer/models/harFile';
import { Sanitizer } from './components/Sanitizer/Sanitizer';

const title = 'HAR file sanitizer (preview)';

const logIssueLinkeStyle: React.CSSProperties = {
margin: '30px'
}

function App() {
const [downloadUrl, setDownloadUrl] = useState('');
const [fileName, setFileName] = useState('');
const [sanitizationCategories, setSanitizationCategories] = useState<SanitizationCategories>({
cookiesAndHeaders: true,
authorizationTokens: true,
armPostResponses: true,
generalJsonResponses: true,
generalJsonPutPostRequests: true
});
const [inspectFile, setInspectFile] = useState(false);
const [sanitizedFileJson, setSanitizedFileJson] = useState<HarFile | null>(null);

useEffect(() => {
document.title = title;
}, [])

const onChecked = (event: FormEvent<HTMLInputElement | HTMLElement> | undefined, checked?: boolean | undefined) => {
if (event) {
const newRulesToRun = { ...sanitizationCategories };
if (newRulesToRun[event.currentTarget.id] === undefined) {
throw Error(`Could not find property: "${event.currentTarget.id}"`);
}

newRulesToRun[event.currentTarget.id] = (event.currentTarget as any).checked;
setSanitizationCategories(newRulesToRun);
}
}

let downloadButton = <></>;
if (downloadUrl) {
downloadButton = <div style={{ marginTop: '10px' }}>
Download <Link href={downloadUrl} download={fileName}> {fileName}</Link>
</div>;
}

return (
<div>
<Stack horizontalAlign="end" horizontal>
<Link href='https://github.com/ehamai/harsanitizer/issues' target='_blank' style={logIssueLinkeStyle}>Log issues</Link>
</Stack>
<Stack enableScopedSelectors horizontalAlign="center" verticalAlign='center' style={layoutStackStyle}>
<Text variant='xxLarge' style={{ position: 'relative', left: '-263px', marginBottom: '10px' }}>{title}</Text>
<div style={containerStyle}>
<Text variant='mediumPlus'>Choose categories to sanitize and then upload a file</Text>
<Stack tokens={stackTokens} styles={radioButtonStackStyle} horizontal wrap>
<Checkbox
label="Cookies and headers"
checked={sanitizationCategories.cookiesAndHeaders}
id={'cookiesAndHeaders'}
onChange={onChecked}
styles={checkboxStyle} />

<Checkbox
label="Authorization Tokens"
checked={sanitizationCategories.authorizationTokens}
id={'authorizationTokens'}
onChange={onChecked}
styles={checkboxStyle} />

<Checkbox
label="JSON PUT and POST Requests"
checked={sanitizationCategories.generalJsonPutPostRequests}
id={'generalJsonPutPostRequests'}
onChange={onChecked}
styles={checkboxStyle} />

<Checkbox
label="ARM Post Responses"
checked={sanitizationCategories.armPostResponses}
id={'armPostResponses'}
onChange={onChecked}
styles={checkboxStyle} />

<Checkbox
label="JSON responses"
checked={sanitizationCategories.generalJsonResponses}
id={'generalJsonResponses'}
onChange={onChecked}
styles={checkboxStyle} />
</Stack>
<div>
<Text>
<input type="file" id="input-file" onChange={(event) =>{ onFileUpload(event, sanitizationCategories, setFileName, setDownloadUrl) }} style={fileUploadStyle} />
{downloadButton}
</Text>
</div>
</div>
</Stack>
</div>
inspectFile ? (
<TraceInspector fileContent={sanitizedFileJson as HarFile}></TraceInspector>
) : (
<div>
<Stack horizontalAlign="end" horizontal>
<Link href='https://github.com/ehamai/harsanitizer/issues' target='_blank' style={logIssueLinkeStyle}>Log issues</Link>
</Stack>
<Sanitizer setInspectFile={setInspectFile} setSanitizedFileJson={setSanitizedFileJson}></Sanitizer>
</div>)
);
}

Expand Down
62 changes: 62 additions & 0 deletions src/common/batchConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { InspectorEntry } from "../components/TraceInspector/TraceInspector";
import { UberBatchRequest, UberBatchResponse } from "../sanitizer/models/batchRequest";
import { Entry, NameValueKeyPair } from "../sanitizer/models/harFile";
import { isBatchRequest } from "../sanitizer/requestRules/armBatchResponseRule";

const convertBatchHeadersToHeaders = (batchHeaders: { [id: string]: string }) => {
const headers: NameValueKeyPair[] = [];
for (const key of Object.keys(batchHeaders)) {
headers.push({
name: key,
value: batchHeaders[key]
});
}

return headers;
}

export const convertBatchEntryToEntries = (entry: Entry, entries: InspectorEntry[]) => {
if (isBatchRequest(entry.request) && entry.request.postData) {
const uberBatchRequest: UberBatchRequest = JSON.parse(entry.request?.postData.text);
const uberBatchResponse: UberBatchResponse = JSON.parse(entry.response.content.text);

for (let i = 0; i < uberBatchRequest.requests.length; i++) {
const batchRequest = uberBatchRequest.requests[i];
const batchResponse = uberBatchResponse.responses[i];
let url = batchRequest.url;
if(url.startsWith('http')){
const parsedUrl = new URL(url);
url = url.split(parsedUrl.origin)[1]; // keep path + query string
}

const newEntry: InspectorEntry = {
request: {
method: batchRequest.httpMethod,
url: url,
headers: convertBatchHeadersToHeaders(batchRequest.requestHeaderDetails),
queryString: [],
cookies: [],
postData: {
mimeType: 'application/json',
text: JSON.stringify(batchRequest.content)
}
},
response: {
status: batchResponse.httpStatusCode,
statusText: '',
headers: convertBatchHeadersToHeaders(batchResponse.headers),
cookies: [],
content: {
mimeType: 'application/json',
text: JSON.stringify(batchResponse.content)
},
_transferSize: batchResponse.contentLength
},
time: 0,
isBatchChildEntry: true
}

entries.push(newEntry);
}
}
}
4 changes: 3 additions & 1 deletion src/common/fileUpload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const getOutputFileNamePrefix = (inputFileName: string) => {
export const onFileUpload = async (event: React.ChangeEvent<HTMLInputElement>,
sanitizationCategories: SanitizationCategories,
setFileName: React.Dispatch<React.SetStateAction<string>>,
setDownloadUrl: React.Dispatch<React.SetStateAction<string>>) => {
setDownloadUrl: React.Dispatch<React.SetStateAction<string>>,
setSanitizedFileJson: React.Dispatch<React.SetStateAction<HarFile | null>>) => {

const input = event.target
if (input.files && input.files.length > 0) {
Expand All @@ -40,6 +41,7 @@ export const onFileUpload = async (event: React.ChangeEvent<HTMLInputElement>,
const content = await readFileContent(input.files[0])
const parsedContent = JSON.parse(content) as HarFile;
sanitize(parsedContent, sanitizationCategories);
setSanitizedFileJson(parsedContent)

const zip = new JSZip();
zip.file(`${fileNamePrefix}.har`, JSON.stringify(parsedContent));
Expand Down
46 changes: 46 additions & 0 deletions src/components/NameValueList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Stack, Text } from "@fluentui/react";
import { NameValueKeyPair } from "../sanitizer/models/harFile";

export interface NameValueListProps{
nameValuePairs: NameValueKeyPair[];
}

const getNameValueContainerStyle = (): React.CSSProperties =>{
return {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
};
}

const lineHeight = '25px'
const getNameStyle =(): React.CSSProperties =>{
return {
minWidth: '150px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
lineHeight: lineHeight,
marginRight: '5px'
}
}

const getValueStyle = (): React.CSSProperties =>{
return { lineHeight: lineHeight };
}

export const NameValueList = (props: NameValueListProps) => {
const { nameValuePairs} = props;

const componentList = [];
for (let i = 0; i < nameValuePairs.length; i++) {
const nameValueComponent = <Stack horizontal key={i} style={getNameValueContainerStyle()}>
<Text style={getNameStyle()}>{nameValuePairs[i].name}</Text>
<Text style={getValueStyle()}>{nameValuePairs[i].value}</Text>
</Stack>

componentList.push(nameValueComponent);
}

return <>{componentList}</>;
}
27 changes: 27 additions & 0 deletions src/components/Sanitizer/Sanitizer.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ICheckboxStyles, IStackStyles } from "@fluentui/react";

const checkboxWidth = 300;

export const checkboxStyle: ICheckboxStyles = {
label: {
width: `${checkboxWidth}px`
}
}

export const layoutStackStyle: React.CSSProperties = {
marginTop: '300px'
}


export const containerStyle: React.CSSProperties = {
border: '1px solid lightgray',
padding: '50px 130px',
borderRadius: '10px'
}

export const radioButtonStackStyle: IStackStyles = {
root: {
width: `${checkboxWidth * 2 + 20}px`,
marginTop: '20px'
},
};
Loading

0 comments on commit 1167da2

Please sign in to comment.