Skip to content

Commit

Permalink
feat(webapp): break adhoc ui upload into 2 steps (#1352)
Browse files Browse the repository at this point in the history
similarly to flamegraph.com, make user upload by clicking on a button, this allows us to force them to add add a name/spyname/unit (not implemented yet)
  • Loading branch information
eh-am authored Aug 5, 2022
1 parent 90c76da commit 9c15298
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 78 deletions.
13 changes: 11 additions & 2 deletions pkg/adhoc/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (s *server) Profiles(w http.ResponseWriter, _ *http.Request) {
return fs.SkipDir
}
if e.Type().IsRegular() {
id := fmt.Sprintf("%x", sha256.Sum256([]byte(e.Name())))
id := s.generateHash(e.Name())
if p, ok := profiles[id]; ok {
return fmt.Errorf("a hash collision detected between %s and %s, please report it", e.Name(), p.Name)
}
Expand Down Expand Up @@ -258,7 +258,12 @@ func (s *server) Upload(w http.ResponseWriter, r *http.Request) {
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(fb); err != nil {
type response struct {
Flamebearer *flamebearer.FlamebearerProfile `json:"flamebearer"`
ID string `json:"id"`
}
res := response{Flamebearer: fb, ID: s.generateHash(filename)}
if err := json.NewEncoder(w).Encode(res); err != nil {
s.log.WithError(err).Error("Unable to encode the response")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -318,3 +323,7 @@ func (s *server) convert(p profile) (*flamebearer.FlamebearerProfile, error) {
}
return converter(b, p.Name, s.maxNodes)
}

func (*server) generateHash(name string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(name)))
}
29 changes: 17 additions & 12 deletions webapp/javascript/pages/adhoc/AdhocComparison.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import 'react-dom';

import { Maybe } from '@webapp/util/fp';
Expand All @@ -14,7 +14,6 @@ import ExportData from '@webapp/components/ExportData';
import {
fetchAllProfiles,
fetchProfile,
selectAdhocUploadedFilename,
selectedSelectedProfileId,
selectProfile,
selectShared,
Expand All @@ -27,8 +26,8 @@ import FileUploader from './components/FileUploader';
function AdhocComparison() {
const dispatch = useAppDispatch();

const leftFilename = useAppSelector(selectAdhocUploadedFilename('left'));
const rightFilename = useAppSelector(selectAdhocUploadedFilename('right'));
const [tabIndexLeft, setTabIndexLeft] = useState(0);
const [tabIndexRight, setTabIndexRight] = useState(0);

const leftProfile = useAppSelector(selectProfile('left'));
const rightProfile = useAppSelector(selectProfile('right'));
Expand Down Expand Up @@ -95,17 +94,20 @@ function AdhocComparison() {
data-testid="comparison-container"
>
<Box className={adhocComparisonStyles.comparisonPane}>
<Tabs>
<Tabs
selectedIndex={tabIndexLeft}
onSelect={(index) => setTabIndexLeft(index)}
>
<TabList>
<Tab>Upload</Tab>
<Tab>Pyroscope data</Tab>
</TabList>
<TabPanel>
<FileUploader
filename={leftFilename}
className={adhocStyles.tabPanel}
setFile={(file) => {
dispatch(uploadFile({ file, side: 'left' }));
setFile={async (file) => {
await dispatch(uploadFile({ file, side: 'left' }));
setTabIndexLeft(1);
}}
/>
</TabPanel>
Expand All @@ -126,22 +128,25 @@ function AdhocComparison() {
</Box>
{/* Right side */}
<Box className={adhocComparisonStyles.comparisonPane}>
<Tabs>
<Tabs
selectedIndex={tabIndexRight}
onSelect={(index) => setTabIndexRight(index)}
>
<TabList>
<Tab>Upload</Tab>
<Tab>Pyroscope data</Tab>
</TabList>
<TabPanel>
<FileUploader
className={adhocStyles.tabPanel}
filename={rightFilename}
setFile={(file) => {
dispatch(
setFile={async (file) => {
await dispatch(
uploadFile({
file,
side: 'right',
})
);
setTabIndexRight(1);
}}
/>
</TabPanel>
Expand Down
38 changes: 33 additions & 5 deletions webapp/javascript/pages/adhoc/AdhocDiff.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import 'react-dom';

import { Maybe } from '@webapp/util/fp';
Expand All @@ -19,9 +19,11 @@ import {
selectProfileId,
selectShared,
selectDiffProfile,
uploadFile,
} from '@webapp/redux/reducers/adhoc';
import adhocStyles from './Adhoc.module.scss';
import adhocComparisonStyles from './AdhocComparison.module.scss';
import FileUploader from './components/FileUploader';

function AdhocDiff() {
const dispatch = useAppDispatch();
Expand All @@ -39,6 +41,8 @@ function AdhocDiff() {
const exportToFlamegraphDotComFn = useExportToFlamegraphDotCom(
diffProfile.unwrapOr(undefined)
);
const [tabIndexLeft, setTabIndexLeft] = useState(0);
const [tabIndexRight, setTabIndexRight] = useState(0);

useEffect(() => {
dispatch(fetchAllProfiles());
Expand Down Expand Up @@ -92,11 +96,23 @@ function AdhocDiff() {
data-testid="comparison-container"
>
<Box className={adhocComparisonStyles.comparisonPane}>
<Tabs>
<Tabs
selectedIndex={tabIndexLeft}
onSelect={(index) => setTabIndexLeft(index)}
>
<TabList>
<Tab>Upload</Tab>
<Tab>Pyroscope data</Tab>
<Tab disabled>Upload</Tab>
</TabList>
<TabPanel>
<FileUploader
className={adhocStyles.tabPanel}
setFile={async (file) => {
await dispatch(uploadFile({ file, side: 'left' }));
setTabIndexLeft(1);
}}
/>
</TabPanel>
<TabPanel>
{profilesList.type === 'loaded' && (
<FileList
Expand All @@ -113,11 +129,23 @@ function AdhocDiff() {
</Tabs>
</Box>
<Box className={adhocComparisonStyles.comparisonPane}>
<Tabs>
<Tabs
selectedIndex={tabIndexRight}
onSelect={(index) => setTabIndexRight(index)}
>
<TabList>
<Tab>Upload</Tab>
<Tab>Pyroscope data</Tab>
<Tab disabled>Upload</Tab>
</TabList>
<TabPanel>
<FileUploader
className={adhocStyles.tabPanel}
setFile={async (file) => {
await dispatch(uploadFile({ file, side: 'right' }));
setTabIndexRight(1);
}}
/>
</TabPanel>
<TabPanel>
{profilesList.type === 'loaded' && (
<FileList
Expand Down
13 changes: 6 additions & 7 deletions webapp/javascript/pages/adhoc/AdhocSingle.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import 'react-dom';

import { useAppDispatch, useAppSelector } from '@webapp/redux/hooks';
Expand All @@ -11,7 +11,6 @@ import useExportToFlamegraphDotCom from '@webapp/components/exportToFlamegraphDo
import ExportData from '@webapp/components/ExportData';
import {
uploadFile,
selectAdhocUploadedFilename,
fetchProfile,
selectShared,
fetchAllProfiles,
Expand All @@ -23,10 +22,10 @@ import adhocStyles from './Adhoc.module.scss';

function AdhocSingle() {
const dispatch = useAppDispatch();
const filename = useAppSelector(selectAdhocUploadedFilename('left'));
const { profilesList } = useAppSelector(selectShared);
const selectedProfileId = useAppSelector(selectedSelectedProfileId('left'));
const profile = useAppSelector(selectProfile('left'));
const [tabIndex, setTabIndex] = useState(0);

useEffect(() => {
dispatch(fetchAllProfiles());
Expand Down Expand Up @@ -60,17 +59,17 @@ function AdhocSingle() {
return (
<div className="main-wrapper">
<Box>
<Tabs>
<Tabs selectedIndex={tabIndex} onSelect={(index) => setTabIndex(index)}>
<TabList>
<Tab>Upload</Tab>
<Tab>Pyroscope data</Tab>
</TabList>
<TabPanel>
<FileUploader
className={adhocStyles.tabPanel}
filename={filename}
setFile={(file) => {
dispatch(uploadFile({ file, side: 'left' }));
setFile={async (file) => {
await dispatch(uploadFile({ file, side: 'left' }));
setTabIndex(1);
}}
/>
</TabPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@
justify-content: space-evenly;
min-height: 130px;
}

.uploadWrapper {
text-align: center;
}
73 changes: 44 additions & 29 deletions webapp/javascript/pages/adhoc/components/FileUploader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import React, { useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { Maybe } from '@webapp/util/fp';
import Button from '@webapp/ui/Button';
import type { DropzoneOptions } from 'react-dropzone';

// Note: I wanted to use https://fontawesome.com/v6.0/icons/arrow-up-from-bracket?s=solid
Expand All @@ -11,11 +11,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import styles from './FileUploader.module.scss';

interface Props {
filename: Maybe<string>;
setFile: (file: File) => void;
className?: string;
}
export default function FileUploader({ filename, setFile, className }: Props) {
export default function FileUploader({ setFile: onUpload, className }: Props) {
const [file, setFile] = React.useState<File>();
type onDrop = Required<DropzoneOptions>['onDrop'];
const onDrop = useCallback<onDrop>(
(acceptedFiles) => {
Expand All @@ -36,34 +36,49 @@ export default function FileUploader({ filename, setFile, className }: Props) {
});

return (
<section className={`${styles.container} ${className}`}>
<div {...getRootProps()} className={styles.dragAndDropContainer}>
<input {...getInputProps()} />
{filename.isJust ? (
<div className={styles.subHeadingContainer}>
<div className={styles.subHeading}>
To analyze another file, drag and drop pprof, json, or collapsed
files here or click to select a file
<>
<section className={`${styles.container} ${className}`}>
<div {...getRootProps()} className={styles.dragAndDropContainer}>
<input {...getInputProps()} />
{file ? (
<div className={styles.subHeadingContainer}>
<div className={styles.subHeading}>
To analyze another file, drag and drop pprof, json, or collapsed
files here or click to select a file
</div>
<div className={styles.headerMain}> {file.name} </div>
</div>
<div className={styles.headerMain}> {filename.value} </div>
</div>
) : (
<div>
<p className={styles.headerMain}>
Drag and drop pprof, json, or collapsed files here
</p>
<div className={styles.iconContainer}>
<FontAwesomeIcon
icon={faArrowAltCircleUp}
className={styles.fileUploadIcon}
/>
) : (
<div>
<p className={styles.headerMain}>
Drag and drop pprof, json, or collapsed files here
</p>
<div className={styles.iconContainer}>
<FontAwesomeIcon
icon={faArrowAltCircleUp}
className={styles.fileUploadIcon}
/>
</div>
<p className={styles.subHeading}>
Or click to select a file from your device
</p>
</div>
<p className={styles.subHeading}>
Or click to select a file from your device
</p>
</div>
)}
)}
</div>
</section>
<div className={styles.uploadWrapper}>
<Button
kind="primary"
disabled={!file}
onClick={() => {
if (file) {
onUpload(file);
}
}}
>
Save
</Button>
</div>
</section>
</>
);
}
Loading

0 comments on commit 9c15298

Please sign in to comment.