mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 13:39:22 +01:00
fix linting & add comment
This commit is contained in:
parent
3972d067fb
commit
98a4722be3
@ -1,5 +1,5 @@
|
||||
import { IconX } from '@posthog/icons'
|
||||
import { LemonButton, Tooltip } from '@posthog/lemon-ui'
|
||||
import { LemonButton } from '@posthog/lemon-ui'
|
||||
import clsx from 'clsx'
|
||||
import { useActions, useValues } from 'kea'
|
||||
|
||||
@ -31,9 +31,13 @@ export function SidePanelPaneHeader({ children, title }: SidePanelPaneHeaderProp
|
||||
</h3>
|
||||
) : null}
|
||||
{children}
|
||||
<Tooltip placement={modalMode ? 'top' : 'bottom-end'} title={modalMode ? 'Close' : 'Close this side panel'}>
|
||||
<LemonButton size="small" sideIcon={<IconX />} onClick={() => closeSidePanel()} />
|
||||
</Tooltip>
|
||||
<LemonButton
|
||||
size="small"
|
||||
sideIcon={<IconX />}
|
||||
onClick={() => closeSidePanel()}
|
||||
tooltip={modalMode ? 'Close' : 'Close this side panel'}
|
||||
tooltipPlacement={modalMode ? 'top' : 'bottom-end'}
|
||||
/>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import clsx from 'clsx'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { LemonButton } from 'lib/lemon-ui/LemonButton'
|
||||
import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture'
|
||||
import { Tooltip } from 'lib/lemon-ui/Tooltip'
|
||||
import { UploadedLogo } from 'lib/lemon-ui/UploadedLogo'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
import { inviteLogic } from 'scenes/settings/organization/inviteLogic'
|
||||
@ -85,27 +84,27 @@ function CurrentOrganization({ organization }: { organization: OrganizationBasic
|
||||
const { closeAccountPopover } = useActions(navigationLogic)
|
||||
|
||||
return (
|
||||
<Tooltip title="Organization settings" placement="left">
|
||||
<LemonButton
|
||||
data-attr="top-menu-item-org-settings"
|
||||
icon={
|
||||
<UploadedLogo
|
||||
name={organization.name}
|
||||
entityId={organization.id}
|
||||
mediaId={organization.logo_media_id}
|
||||
/>
|
||||
}
|
||||
sideIcon={<IconGear />}
|
||||
fullWidth
|
||||
to={urls.settings('organization')}
|
||||
onClick={closeAccountPopover}
|
||||
>
|
||||
<div className="grow">
|
||||
<span className="font-medium">{organization.name}</span>
|
||||
<AccessLevelIndicator organization={organization} />
|
||||
</div>
|
||||
</LemonButton>
|
||||
</Tooltip>
|
||||
<LemonButton
|
||||
data-attr="top-menu-item-org-settings"
|
||||
icon={
|
||||
<UploadedLogo
|
||||
name={organization.name}
|
||||
entityId={organization.id}
|
||||
mediaId={organization.logo_media_id}
|
||||
/>
|
||||
}
|
||||
sideIcon={<IconGear />}
|
||||
fullWidth
|
||||
to={urls.settings('organization')}
|
||||
onClick={closeAccountPopover}
|
||||
tooltip="Organization settings"
|
||||
tooltipPlacement="left"
|
||||
>
|
||||
<div className="grow">
|
||||
<span className="font-medium">{organization.name}</span>
|
||||
<AccessLevelIndicator organization={organization} />
|
||||
</div>
|
||||
</LemonButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { ActivityLogItem } from 'lib/components/ActivityLog/humanizeActivity'
|
||||
import { apiStatusLogic } from 'lib/logic/apiStatusLogic'
|
||||
import { objectClean, toParams } from 'lib/utils'
|
||||
import posthog from 'posthog-js'
|
||||
import { ErrorTrackingSymbolSet } from 'scenes/error-tracking/errorTrackingConfigurationSceneLogic'
|
||||
import { ErrorTrackingSymbolSet } from 'scenes/error-tracking/errorTrackingSymbolSetLogic'
|
||||
import { stringifiedFingerprint } from 'scenes/error-tracking/utils'
|
||||
import { RecordingComment } from 'scenes/session-recordings/player/inspector/playerInspectorLogic'
|
||||
import { SavedSessionRecordingPlaylistsResult } from 'scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic'
|
||||
@ -726,10 +726,6 @@ class ApiRequest {
|
||||
return this.errorTrackingSymbolSets().addPathComponent('missing')
|
||||
}
|
||||
|
||||
public errorTrackingStackFrames(ids: string[]): ApiRequest {
|
||||
return this.errorTracking().addPathComponent('stack_frames').withQueryString({ ids })
|
||||
}
|
||||
|
||||
// # Warehouse
|
||||
public dataWarehouseTables(teamId?: TeamType['id']): ApiRequest {
|
||||
return this.projectsDetail(teamId).addPathComponent('warehouse_tables')
|
||||
@ -1872,10 +1868,6 @@ const api = {
|
||||
async missingSymbolSets(): Promise<ErrorTrackingSymbolSet[]> {
|
||||
return await new ApiRequest().errorTrackingMissingSymbolSets().get()
|
||||
},
|
||||
|
||||
async fetchStackFrames(ids: string[]): Promise<{ content: string }> {
|
||||
return await new ApiRequest().errorTrackingStackFrames(ids).get()
|
||||
},
|
||||
},
|
||||
|
||||
recordings: {
|
||||
|
@ -1,13 +1,15 @@
|
||||
import './ErrorDisplay.scss'
|
||||
|
||||
import { IconFlag } from '@posthog/icons'
|
||||
import { LemonCollapse } from '@posthog/lemon-ui'
|
||||
import { IconFlag, IconUpload } from '@posthog/icons'
|
||||
import { LemonButton, LemonCollapse } from '@posthog/lemon-ui'
|
||||
import { useActions } from 'kea'
|
||||
import { TitledSnack } from 'lib/components/TitledSnack'
|
||||
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
|
||||
import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch'
|
||||
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
|
||||
import { Link } from 'lib/lemon-ui/Link'
|
||||
import { useState } from 'react'
|
||||
import { errorTrackingSymbolSetLogic } from 'scenes/error-tracking/errorTrackingSymbolSetLogic'
|
||||
|
||||
import { EventType } from '~/types'
|
||||
|
||||
@ -29,29 +31,53 @@ interface Exception {
|
||||
value: string
|
||||
}
|
||||
|
||||
function StackTrace({ frames, showAllFrames }: { frames: StackFrame[]; showAllFrames: boolean }): JSX.Element | null {
|
||||
function StackTrace({
|
||||
frames,
|
||||
showAllFrames,
|
||||
resolved,
|
||||
}: {
|
||||
frames: StackFrame[]
|
||||
showAllFrames: boolean
|
||||
resolved: boolean
|
||||
}): JSX.Element | null {
|
||||
const { setUploadSymbolSetReference } = useActions(errorTrackingSymbolSetLogic)
|
||||
const displayFrames = showAllFrames ? frames : frames.filter((f) => f.in_app)
|
||||
|
||||
const panels = displayFrames.map(({ filename, lineno, colno, function: functionName }, index) => {
|
||||
return {
|
||||
key: index,
|
||||
header: (
|
||||
<div className="flex flex-wrap space-x-0.5">
|
||||
<span>{filename}</span>
|
||||
{functionName ? (
|
||||
<div className="flex space-x-0.5">
|
||||
<span className="text-muted">in</span>
|
||||
<span>{functionName}</span>
|
||||
</div>
|
||||
) : null}
|
||||
{lineno && colno ? (
|
||||
<div className="flex space-x-0.5">
|
||||
<span className="text-muted">at line</span>
|
||||
<div className="flex flex-1 justify-between items-center">
|
||||
<div className="flex flex-wrap space-x-0.5">
|
||||
<span>{filename}</span>
|
||||
{functionName ? (
|
||||
<div className="flex space-x-0.5">
|
||||
<span className="text-muted">in</span>
|
||||
<span>{functionName}</span>
|
||||
</div>
|
||||
) : null}
|
||||
{lineno && colno ? (
|
||||
<div className="flex space-x-0.5">
|
||||
<span className="text-muted">at line</span>
|
||||
<span>
|
||||
{lineno}:{colno}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{!resolved && (
|
||||
<div className="flex items-center space-x-1">
|
||||
<span>
|
||||
{lineno}:{colno}
|
||||
<LemonTag>Unresolved</LemonTag>
|
||||
</span>
|
||||
<LemonButton
|
||||
icon={<IconUpload />}
|
||||
size="xsmall"
|
||||
onClick={() => setUploadSymbolSetReference(filename)}
|
||||
tooltip="Upload source map"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
content: null,
|
||||
@ -78,7 +104,7 @@ function ChainedStackTraces({ exceptionList }: { exceptionList: Exception[] }):
|
||||
/>
|
||||
</div>
|
||||
{exceptionList.map(({ stacktrace, value }, index) => {
|
||||
const { frames } = stacktrace || {}
|
||||
const { frames, type } = stacktrace || {}
|
||||
if (!showAllFrames && !frames?.some((frame) => frame.in_app)) {
|
||||
// if we're not showing all frames and there are no in_app frames, skip this exception
|
||||
return null
|
||||
@ -87,7 +113,11 @@ function ChainedStackTraces({ exceptionList }: { exceptionList: Exception[] }):
|
||||
return (
|
||||
<div key={index} className="ErrorDisplay__stacktrace flex flex-col gap-1 mt-6">
|
||||
<h3 className="mb-0">{value}</h3>
|
||||
<StackTrace frames={frames || []} showAllFrames={showAllFrames} />
|
||||
<StackTrace
|
||||
frames={frames || []}
|
||||
resolved={type === 'resolved'}
|
||||
showAllFrames={showAllFrames}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
@ -1,9 +1,3 @@
|
||||
import { kea, path } from 'kea'
|
||||
import { loaders } from 'kea-loaders'
|
||||
import api from 'lib/api'
|
||||
|
||||
import type { stackFrameLogicType } from './stackFrameLogicType'
|
||||
|
||||
export interface StackFrame {
|
||||
filename: string
|
||||
lineno: number
|
||||
@ -11,20 +5,3 @@ export interface StackFrame {
|
||||
function: string
|
||||
in_app?: boolean
|
||||
}
|
||||
|
||||
export const stackFrameLogic = kea<stackFrameLogicType>([
|
||||
path(['components', 'Errors', 'stackFrameLogic']),
|
||||
loaders(({ values }) => ({
|
||||
stackFrames: [
|
||||
{} as Record<string, StackFrame>,
|
||||
{
|
||||
loadFrames: async ({ frameIds }: { frameIds: string[] }) => {
|
||||
const loadedFrameIds = Object.keys(values.stackFrames)
|
||||
const ids = frameIds.filter((id) => loadedFrameIds.includes(id))
|
||||
await api.errorTracking.fetchStackFrames(ids)
|
||||
return {}
|
||||
},
|
||||
},
|
||||
],
|
||||
})),
|
||||
])
|
||||
|
@ -1,25 +1,30 @@
|
||||
import { IconUpload } from '@posthog/icons'
|
||||
import { LemonButton, LemonFileInput, LemonModal, LemonTable } from '@posthog/lemon-ui'
|
||||
import { LemonButton, LemonTable } from '@posthog/lemon-ui'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Form } from 'kea-forms'
|
||||
import { IconUploadFile } from 'lib/lemon-ui/icons'
|
||||
import { LemonField } from 'lib/lemon-ui/LemonField'
|
||||
import { SceneExport } from 'scenes/sceneTypes'
|
||||
|
||||
import { errorTrackingConfigurationSceneLogic } from './errorTrackingConfigurationSceneLogic'
|
||||
import { errorTrackingSymbolSetLogic } from './errorTrackingSymbolSetLogic'
|
||||
import { SymbolSetUploadModal } from './SymbolSetUploadModal'
|
||||
|
||||
export const scene: SceneExport = {
|
||||
component: ErrorTrackingConfigurationScene,
|
||||
logic: errorTrackingConfigurationSceneLogic,
|
||||
logic: errorTrackingSymbolSetLogic,
|
||||
}
|
||||
|
||||
export function ErrorTrackingConfigurationScene(): JSX.Element {
|
||||
const { missingSymbolSets, missingSymbolSetsLoading } = useValues(errorTrackingConfigurationSceneLogic)
|
||||
const { setUploadSymbolSetReference } = useActions(errorTrackingConfigurationSceneLogic)
|
||||
const { setUploadSymbolSetReference } = useActions(errorTrackingSymbolSetLogic)
|
||||
const { missingSymbolSets, missingSymbolSetsLoading } = useValues(errorTrackingSymbolSetLogic)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Missing symbol sets</h2>
|
||||
<p>
|
||||
Source maps are required to demangle any minified code in your exception stack traces. PostHog
|
||||
automatically retrieves source maps where possible. Cases where it was not possible are listed below.
|
||||
Source maps can be uploaded retroactively but changes will only apply to all future exceptions ingested.
|
||||
</p>
|
||||
<LemonTable
|
||||
showHeader={false}
|
||||
columns={[
|
||||
{ title: 'Reference', dataIndex: 'ref' },
|
||||
{
|
||||
@ -43,60 +48,7 @@ export function ErrorTrackingConfigurationScene(): JSX.Element {
|
||||
loading={missingSymbolSetsLoading}
|
||||
dataSource={missingSymbolSets}
|
||||
/>
|
||||
<SymbolSetUploadModal onClose={() => setUploadSymbolSetReference(null)} />
|
||||
<SymbolSetUploadModal />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const SymbolSetUploadModal = ({ onClose }: { onClose: () => void }): JSX.Element => {
|
||||
const { uploadSymbolSetReference, isUploadSymbolSetSubmitting, uploadSymbolSet } = useValues(
|
||||
errorTrackingConfigurationSceneLogic
|
||||
)
|
||||
|
||||
return (
|
||||
<LemonModal title="" onClose={onClose} isOpen={!!uploadSymbolSetReference} simple>
|
||||
<Form
|
||||
logic={errorTrackingConfigurationSceneLogic}
|
||||
formKey="uploadSymbolSet"
|
||||
className="gap-1"
|
||||
enableFormOnSubmit
|
||||
>
|
||||
<LemonModal.Header>
|
||||
<h3>Upload source map</h3>
|
||||
</LemonModal.Header>
|
||||
<LemonModal.Content className="space-y-2">
|
||||
<LemonField name="files">
|
||||
<LemonFileInput
|
||||
accept="text/plain"
|
||||
multiple={false}
|
||||
callToAction={
|
||||
<div className="flex flex-col items-center justify-center space-y-2 border border-dashed rounded p-4">
|
||||
<span className="flex items-center gap-2 font-semibold">
|
||||
<IconUploadFile className="text-2xl" /> Add source map
|
||||
</span>
|
||||
<div>
|
||||
Drag and drop your local source map here or click to open the file browser.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</LemonField>
|
||||
</LemonModal.Content>
|
||||
<LemonModal.Footer>
|
||||
<LemonButton type="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
</LemonButton>
|
||||
<LemonButton
|
||||
disabledReason={uploadSymbolSet.files.length < 1 ? 'Upload a source map' : undefined}
|
||||
type="primary"
|
||||
status="alt"
|
||||
htmlType="submit"
|
||||
loading={isUploadSymbolSetSubmitting}
|
||||
>
|
||||
Upload
|
||||
</LemonButton>
|
||||
</LemonModal.Footer>
|
||||
</Form>
|
||||
</LemonModal>
|
||||
)
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { AssigneeSelect } from './AssigneeSelect'
|
||||
import ErrorTrackingFilters from './ErrorTrackingFilters'
|
||||
import { errorTrackingGroupSceneLogic } from './errorTrackingGroupSceneLogic'
|
||||
import { OverviewTab } from './groups/OverviewTab'
|
||||
import { SymbolSetUploadModal } from './SymbolSetUploadModal'
|
||||
|
||||
export const scene: SceneExport = {
|
||||
component: ErrorTrackingGroupScene,
|
||||
@ -82,6 +83,7 @@ export function ErrorTrackingGroupScene(): JSX.Element {
|
||||
<LemonDivider className="mt-2" />
|
||||
<ErrorTrackingFilters.Options isGroup />
|
||||
<OverviewTab />
|
||||
<SymbolSetUploadModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
59
frontend/src/scenes/error-tracking/SymbolSetUploadModal.tsx
Normal file
59
frontend/src/scenes/error-tracking/SymbolSetUploadModal.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { LemonButton, LemonFileInput, LemonModal } from '@posthog/lemon-ui'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Form } from 'kea-forms'
|
||||
import { IconUploadFile } from 'lib/lemon-ui/icons'
|
||||
import { LemonField } from 'lib/lemon-ui/LemonField'
|
||||
|
||||
import { errorTrackingSymbolSetLogic } from './errorTrackingSymbolSetLogic'
|
||||
|
||||
export const SymbolSetUploadModal = (): JSX.Element => {
|
||||
const { setUploadSymbolSetReference } = useActions(errorTrackingSymbolSetLogic)
|
||||
const { uploadSymbolSetReference, isUploadSymbolSetSubmitting, uploadSymbolSet } =
|
||||
useValues(errorTrackingSymbolSetLogic)
|
||||
|
||||
const onClose = (): void => {
|
||||
setUploadSymbolSetReference(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<LemonModal title="" onClose={onClose} isOpen={!!uploadSymbolSetReference} simple>
|
||||
<Form logic={errorTrackingSymbolSetLogic} formKey="uploadSymbolSet" className="gap-1" enableFormOnSubmit>
|
||||
<LemonModal.Header>
|
||||
<h3>Upload source map</h3>
|
||||
</LemonModal.Header>
|
||||
<LemonModal.Content className="space-y-2">
|
||||
<LemonField name="files">
|
||||
<LemonFileInput
|
||||
accept="text/plain"
|
||||
multiple={false}
|
||||
callToAction={
|
||||
<div className="flex flex-col items-center justify-center space-y-2 border border-dashed rounded p-4">
|
||||
<span className="flex items-center gap-2 font-semibold">
|
||||
<IconUploadFile className="text-2xl" /> Add source map
|
||||
</span>
|
||||
<div>
|
||||
Drag and drop your local source map here or click to open the file browser.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</LemonField>
|
||||
</LemonModal.Content>
|
||||
<LemonModal.Footer>
|
||||
<LemonButton type="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
</LemonButton>
|
||||
<LemonButton
|
||||
disabledReason={uploadSymbolSet.files.length < 1 ? 'Upload a source map' : undefined}
|
||||
type="primary"
|
||||
status="alt"
|
||||
htmlType="submit"
|
||||
loading={isUploadSymbolSetSubmitting}
|
||||
>
|
||||
Upload
|
||||
</LemonButton>
|
||||
</LemonModal.Footer>
|
||||
</Form>
|
||||
</LemonModal>
|
||||
)
|
||||
}
|
@ -8,7 +8,7 @@ import { urls } from 'scenes/urls'
|
||||
|
||||
import { Breadcrumb } from '~/types'
|
||||
|
||||
import type { errorTrackingConfigurationSceneLogicType } from './errorTrackingConfigurationSceneLogicType'
|
||||
import type { errorTrackingSymbolSetLogicType } from './errorTrackingSymbolSetLogicType'
|
||||
|
||||
export enum ErrorGroupTab {
|
||||
Overview = 'overview',
|
||||
@ -19,8 +19,8 @@ export interface ErrorTrackingSymbolSet {
|
||||
ref: string
|
||||
}
|
||||
|
||||
export const errorTrackingConfigurationSceneLogic = kea<errorTrackingConfigurationSceneLogicType>([
|
||||
path((key) => ['scenes', 'error-tracking', 'errorTrackingConfigurationSceneLogic', key]),
|
||||
export const errorTrackingSymbolSetLogic = kea<errorTrackingSymbolSetLogicType>([
|
||||
path(['scenes', 'error-tracking', 'errorTrackingSymbolSetLogic']),
|
||||
|
||||
actions({
|
||||
setUploadSymbolSetReference: (ref: string | null) => ({ ref }),
|
@ -63,6 +63,7 @@ class ErrorTrackingSymbolSetViewSet(TeamAndOrgViewSetMixin, mixins.UpdateModelMi
|
||||
storage_ptr = upload_symbol_set(request.FILES["source_map"], self.team_id)
|
||||
symbol_set.storage_ptr = storage_ptr
|
||||
symbol_set.save()
|
||||
# TODO: cascade delete the associated frame resolutions
|
||||
return Response({"ok": True}, status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@extend_schema(exclude=True)
|
||||
|
Loading…
Reference in New Issue
Block a user