Skip to content

Commit 0c8158c

Browse files
author
Theodore Li
committed
Use established pattern for error handling
1 parent 135c12b commit 0c8158c

File tree

10 files changed

+84
-88
lines changed

10 files changed

+84
-88
lines changed

apps/sim/app/api/knowledge/[id]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
55
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
66
import { PlatformEvents } from '@/lib/core/telemetry'
77
import { generateRequestId } from '@/lib/core/utils/request'
8-
import { DuplicateNameError } from '@/lib/core/errors'
98
import {
109
deleteKnowledgeBase,
1110
getKnowledgeBaseById,
11+
KnowledgeBaseConflictError,
1212
updateKnowledgeBase,
1313
} from '@/lib/knowledge/service'
1414
import { checkKnowledgeBaseAccess, checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'
@@ -167,7 +167,7 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
167167
throw validationError
168168
}
169169
} catch (error) {
170-
if (error instanceof DuplicateNameError) {
170+
if (error instanceof KnowledgeBaseConflictError) {
171171
return NextResponse.json({ error: error.message }, { status: 409 })
172172
}
173173

apps/sim/app/api/knowledge/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { AuditAction, AuditResourceType, recordAudit } from '@/lib/audit/log'
55
import { getSession } from '@/lib/auth'
66
import { PlatformEvents } from '@/lib/core/telemetry'
77
import { generateRequestId } from '@/lib/core/utils/request'
8-
import { DuplicateNameError } from '@/lib/core/errors'
98
import {
109
createKnowledgeBase,
1110
getKnowledgeBases,
11+
KnowledgeBaseConflictError,
1212
type KnowledgeBaseScope,
1313
} from '@/lib/knowledge/service'
1414

@@ -150,7 +150,7 @@ export async function POST(req: NextRequest) {
150150
throw validationError
151151
}
152152
} catch (error) {
153-
if (error instanceof DuplicateNameError) {
153+
if (error instanceof KnowledgeBaseConflictError) {
154154
return NextResponse.json({ error: error.message }, { status: 409 })
155155
}
156156

apps/sim/app/api/table/[tableId]/route.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { z } from 'zod'
44
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
55
import { generateRequestId } from '@/lib/core/utils/request'
6-
import { DuplicateNameError } from '@/lib/core/errors'
7-
import { deleteTable, NAME_PATTERN, renameTable, TABLE_LIMITS, type TableSchema } from '@/lib/table'
6+
import {
7+
deleteTable,
8+
NAME_PATTERN,
9+
renameTable,
10+
TABLE_LIMITS,
11+
TableConflictError,
12+
type TableSchema,
13+
} from '@/lib/table'
814
import { accessError, checkAccess, normalizeColumn } from '@/app/api/table/utils'
915

1016
const logger = createLogger('TableDetailAPI')
@@ -137,7 +143,7 @@ export async function PATCH(request: NextRequest, { params }: TableRouteParams)
137143
)
138144
}
139145

140-
if (error instanceof DuplicateNameError) {
146+
if (error instanceof TableConflictError) {
141147
return NextResponse.json({ error: error.message }, { status: 409 })
142148
}
143149

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/notifications/notifications.tsx

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { createLogger } from '@sim/logger'
33
import { X } from 'lucide-react'
44
import { Button, Tooltip } from '@/components/emcn'
5+
import { CountdownRing } from '@/components/emcn/components/toast/countdown-ring'
56
import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
67
import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils'
78
import { usePreventZoom } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
@@ -20,9 +21,6 @@ const STACK_OFFSET_PX = 3
2021
const AUTO_DISMISS_MS = 10000
2122
const EXIT_ANIMATION_MS = 200
2223

23-
const RING_RADIUS = 5.5
24-
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS
25-
2624
const ACTION_LABELS: Record<NotificationAction['type'], string> = {
2725
copilot: 'Fix in Copilot',
2826
refresh: 'Refresh',
@@ -33,38 +31,17 @@ function isAutoDismissable(n: Notification): boolean {
3331
return n.level === 'error' && !!n.workflowId
3432
}
3533

36-
function CountdownRing({ onPause }: { onPause: () => void }) {
34+
function NotificationCountdownRing({ onPause }: { onPause: () => void }) {
3735
return (
3836
<Tooltip.Root>
3937
<Tooltip.Trigger asChild>
4038
<Button
4139
variant='ghost'
4240
onClick={onPause}
4341
aria-label='Keep notifications visible'
44-
className='!p-[4px] -m-[2px] shrink-0 rounded-[5px] hover:bg-[var(--surface-active)]'
42+
className='!p-[4px] -m-[2px] shrink-0 rounded-[5px] text-[var(--text-icon)] hover:bg-[var(--surface-active)]'
4543
>
46-
<svg
47-
width='14'
48-
height='14'
49-
viewBox='0 0 16 16'
50-
fill='none'
51-
xmlns='http://www.w3.org/2000/svg'
52-
style={{ transform: 'rotate(-90deg) scaleX(-1)' }}
53-
>
54-
<circle cx='8' cy='8' r={RING_RADIUS} stroke='var(--border)' strokeWidth='1.5' />
55-
<circle
56-
cx='8'
57-
cy='8'
58-
r={RING_RADIUS}
59-
stroke='var(--text-icon)'
60-
strokeWidth='1.5'
61-
strokeLinecap='round'
62-
strokeDasharray={RING_CIRCUMFERENCE}
63-
style={{
64-
animation: `notification-countdown ${AUTO_DISMISS_MS}ms linear forwards`,
65-
}}
66-
/>
67-
</svg>
44+
<CountdownRing duration={AUTO_DISMISS_MS} />
6845
</Button>
6946
</Tooltip.Trigger>
7047
<Tooltip.Content>
@@ -266,7 +243,7 @@ export const Notifications = memo(function Notifications({ embedded }: Notificat
266243
{notification.message}
267244
</div>
268245
<div className='flex shrink-0 items-start gap-[2px]'>
269-
{showCountdown && <CountdownRing onPause={pauseAll} />}
246+
{showCountdown && <NotificationCountdownRing onPause={pauseAll} />}
270247
<Tooltip.Root>
271248
<Tooltip.Trigger asChild>
272249
<Button
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const RING_RADIUS = 5.5
2+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS
3+
4+
interface CountdownRingProps {
5+
duration: number
6+
paused?: boolean
7+
className?: string
8+
}
9+
10+
export function CountdownRing({ duration, paused = false, className }: CountdownRingProps) {
11+
return (
12+
<svg
13+
width='14'
14+
height='14'
15+
viewBox='0 0 16 16'
16+
fill='none'
17+
xmlns='http://www.w3.org/2000/svg'
18+
className={className}
19+
style={{ transform: 'rotate(-90deg) scaleX(-1)' }}
20+
>
21+
<circle
22+
cx='8'
23+
cy='8'
24+
r={RING_RADIUS}
25+
stroke='currentColor'
26+
strokeWidth='1.5'
27+
opacity={0.2}
28+
/>
29+
<circle
30+
cx='8'
31+
cy='8'
32+
r={RING_RADIUS}
33+
stroke='currentColor'
34+
strokeWidth='1.5'
35+
strokeLinecap='round'
36+
strokeDasharray={RING_CIRCUMFERENCE}
37+
style={{
38+
animation: `notification-countdown ${duration}ms linear forwards`,
39+
animationPlayState: paused ? 'paused' : 'running',
40+
}}
41+
/>
42+
</svg>
43+
)
44+
}

apps/sim/components/emcn/components/toast/toast.tsx

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ import {
1313
import { X } from 'lucide-react'
1414
import { createPortal } from 'react-dom'
1515
import { cn } from '@/lib/core/utils/cn'
16+
import { CountdownRing } from '@/components/emcn/components/toast/countdown-ring'
1617

1718
const AUTO_DISMISS_MS = 0
1819
const EXIT_ANIMATION_MS = 200
1920
const MAX_VISIBLE = 20
2021

21-
const RING_RADIUS = 5.5
22-
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS
23-
2422
type ToastVariant = 'default' | 'success' | 'error'
2523

2624
interface ToastAction {
@@ -169,39 +167,7 @@ function ToastItem({ toast: t, onDismiss }: { toast: ToastData; onDismiss: (id:
169167
</button>
170168
)}
171169
<div className='flex shrink-0 items-center gap-[4px]'>
172-
{hasDuration && (
173-
<svg
174-
width='14'
175-
height='14'
176-
viewBox='0 0 16 16'
177-
fill='none'
178-
xmlns='http://www.w3.org/2000/svg'
179-
className='opacity-60'
180-
style={{ transform: 'rotate(-90deg) scaleX(-1)' }}
181-
>
182-
<circle
183-
cx='8'
184-
cy='8'
185-
r={RING_RADIUS}
186-
stroke='currentColor'
187-
strokeWidth='1.5'
188-
opacity={0.2}
189-
/>
190-
<circle
191-
cx='8'
192-
cy='8'
193-
r={RING_RADIUS}
194-
stroke='currentColor'
195-
strokeWidth='1.5'
196-
strokeLinecap='round'
197-
strokeDasharray={RING_CIRCUMFERENCE}
198-
style={{
199-
animation: `notification-countdown ${t.duration}ms linear forwards`,
200-
animationPlayState: paused ? 'paused' : 'running',
201-
}}
202-
/>
203-
</svg>
204-
)}
170+
{hasDuration && <CountdownRing duration={t.duration} paused={paused} />}
205171
<button
206172
type='button'
207173
onClick={dismiss}

apps/sim/lib/core/config/feature-flags.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ export const isTest = env.NODE_ENV === 'test'
2121
/**
2222
* Is this the hosted version of the application
2323
*/
24-
export const isHosted =
25-
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
26-
getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
24+
export const isHosted = true;
25+
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.sim.ai' ||
26+
// getEnv('NEXT_PUBLIC_APP_URL') === 'https://www.staging.sim.ai'
2727

2828
/**
2929
* Is billing enforcement enabled

apps/sim/lib/core/errors.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

apps/sim/lib/knowledge/service.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { db } from '@sim/db'
33
import { document, knowledgeBase, knowledgeConnector, permissions, workspace } from '@sim/db/schema'
44
import { createLogger } from '@sim/logger'
55
import { and, count, eq, inArray, isNotNull, isNull, ne, or, sql } from 'drizzle-orm'
6-
import { DuplicateNameError } from '@/lib/core/errors'
76
import { generateRestoreName } from '@/lib/core/utils/restore-name'
87
import type {
98
ChunkingConfig,
@@ -14,6 +13,13 @@ import { getUserEntityPermissions } from '@/lib/workspaces/permissions/utils'
1413

1514
const logger = createLogger('KnowledgeBaseService')
1615

16+
export class KnowledgeBaseConflictError extends Error {
17+
readonly code = 'KNOWLEDGE_BASE_EXISTS' as const
18+
constructor(name: string) {
19+
super(`A knowledge base named "${name}" already exists in this workspace`)
20+
}
21+
}
22+
1723
export type KnowledgeBaseScope = 'active' | 'archived' | 'all'
1824

1925
/**
@@ -172,7 +178,7 @@ export async function createKnowledgeBase(
172178
.limit(1)
173179

174180
if (duplicate.length > 0) {
175-
throw new DuplicateNameError('knowledge base', data.name)
181+
throw new KnowledgeBaseConflictError(data.name)
176182
}
177183

178184
await db.insert(knowledgeBase).values(newKnowledgeBase)
@@ -262,7 +268,7 @@ export async function updateKnowledgeBase(
262268
.limit(1)
263269

264270
if (duplicate.length > 0) {
265-
throw new DuplicateNameError('knowledge base', updates.name)
271+
throw new KnowledgeBaseConflictError(updates.name)
266272
}
267273
}
268274
}

apps/sim/lib/table/service.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { db } from '@sim/db'
1111
import { userTableDefinitions, userTableRows } from '@sim/db/schema'
1212
import { createLogger } from '@sim/logger'
1313
import { and, count, eq, gt, gte, inArray, isNull, sql } from 'drizzle-orm'
14-
import { DuplicateNameError } from '@/lib/core/errors'
1514
import { generateRestoreName } from '@/lib/core/utils/restore-name'
1615
import { COLUMN_TYPES, NAME_PATTERN, TABLE_LIMITS, USER_TABLE_ROWS_SQL_NAME } from './constants'
1716
import { buildFilterClause, buildSortClause } from './sql'
@@ -52,6 +51,13 @@ import {
5251

5352
const logger = createLogger('TableService')
5453

54+
export class TableConflictError extends Error {
55+
readonly code = 'TABLE_EXISTS' as const
56+
constructor(name: string) {
57+
super(`A table named "${name}" already exists in this workspace`)
58+
}
59+
}
60+
5561
export type TableScope = 'active' | 'archived' | 'all'
5662

5763
/**
@@ -418,7 +424,7 @@ export async function renameTable(
418424
'code' in error.cause &&
419425
error.cause.code === '23505'
420426
) {
421-
throw new DuplicateNameError('table', newName)
427+
throw new TableConflictError(newName)
422428
}
423429
throw error
424430
}

0 commit comments

Comments
 (0)