Skip to content

Commit 5fbc0d2

Browse files
fix(issues): type errors from CI
1 parent e7f7af9 commit 5fbc0d2

7 files changed

Lines changed: 51 additions & 27 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/issues/components/issue-activity.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ interface IssueCommentProps {
141141
function IssueComment({ workspaceId, issueKey, event, author }: IssueCommentProps) {
142142
const { data: session } = useSession()
143143
const deleteComment = useDeleteIssueComment()
144-
const isOwn = Boolean(event.actor?.userId && event.actor.userId === session?.user.id)
144+
const isOwn = Boolean(event.actor?.userId && event.actor.userId === session?.user?.id)
145145
const name = author?.name ?? 'Someone'
146146
return (
147147
<li className='my-2 flex flex-col gap-1.5 rounded-lg border border-[var(--border)] px-3 py-2.5'>

‎apps/sim/app/workspace/[workspaceId]/issues/components/issue-properties.tsx‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
'use client'
22

33
import type { ComponentType } from 'react'
4-
import { Avatar, ChipDropdown, type ChipDropdownOption } from '@sim/emcn'
4+
import { Avatar, AvatarFallback, ChipDropdown, type ChipDropdownOption } from '@sim/emcn'
55
import {
66
Dashboard,
77
Database,
88
File as FileIcon,
99
Link as LinkIcon,
1010
Table as TableIcon,
11-
User,
1211
Workflow,
1312
} from '@sim/emcn/icons'
1413
import { JiraIcon, LinearIcon } from '@/components/icons'
@@ -76,7 +75,15 @@ function OwnerProperty({ workspaceId, issue, canEdit }: PropertyProps) {
7675
const members = useWorkspaceMembersQuery(workspaceId)
7776
const updateIssue = useUpdateIssue()
7877
const options = [
79-
{ value: UNASSIGNED, label: 'Unassigned', icon: User },
78+
{
79+
value: UNASSIGNED,
80+
label: 'Unassigned',
81+
iconElement: (
82+
<Avatar size='xs' aria-hidden>
83+
<AvatarFallback />
84+
</Avatar>
85+
),
86+
},
8087
...(members.data ?? []).map((member) => ({
8188
value: member.userId,
8289
label: member.name,

‎apps/sim/lib/api/contracts/v2/openapi/resources.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ type ResourceTag =
545545
| 'Credentials'
546546
| 'Secrets'
547547
| 'Catalog'
548+
| 'Issues'
548549

549550
function resourceOperation(
550551
tag: ResourceTag,

‎apps/sim/lib/data-drains/sources/copilot-chats.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const chatColumns = {
4545
lastSeenAt: copilotChats.lastSeenAt,
4646
autoAllowedTools: copilotChats.autoAllowedTools,
4747
pinned: copilotChats.pinned,
48+
issueId: copilotChats.issueId,
4849
deletedAt: copilotChats.deletedAt,
4950
createdAt: copilotChats.createdAt,
5051
updatedAt: copilotChats.updatedAt,

‎apps/sim/lib/issues/application/issues.ts‎

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ async function resolveIssueContext({ input }: { input: IssueInput }): Promise<Is
8888
return { ...workspace, issue: row }
8989
}
9090

91-
const authorizeIssues = ({ context }: { context: ActiveWorkspaceApplicationContext }) =>
91+
/** Called from an unannotated `authorizeResource` arrow, so it never narrows the inferred context. */
92+
const authorizeIssues = (context: { workspaceOrganizationId: string | null }) =>
9293
requireIssuesEnabled(context.workspaceOrganizationId)
9394

9495
function validateTitle(title: string): string {
@@ -169,7 +170,7 @@ export const listIssues = defineAuthorizedWorkspaceUseCase({
169170
resolveContext: ({ input }: { input: { workspaceId: string } }) =>
170171
resolveActiveWorkspaceApplicationContext(input.workspaceId),
171172
authorizationOptions,
172-
authorizeResource: authorizeIssues,
173+
authorizeResource: ({ context }) => authorizeIssues(context),
173174
async execute({ context }) {
174175
const rows = await listWorkspaceIssues(context.workspaceId)
175176
return { issues: await presentRows(rows) }
@@ -180,7 +181,7 @@ export const getIssueDetail = defineAuthorizedWorkspaceUseCase({
180181
operation: issueOperations.read,
181182
resolveContext: resolveIssueContext,
182183
authorizationOptions,
183-
authorizeResource: authorizeIssues,
184+
authorizeResource: ({ context }) => authorizeIssues(context),
184185
async execute({ context }) {
185186
const issueId = context.issue.id
186187
const [issue, events, chats, resources, tickets] = await Promise.all([
@@ -219,7 +220,7 @@ export const createIssue = defineAuthorizedWorkspaceUseCase({
219220
resolveContext: ({ input }: { input: CreateIssueInput }) =>
220221
resolveActiveWorkspaceApplicationContext(input.workspaceId),
221222
authorizationOptions,
222-
authorizeResource: authorizeIssues,
223+
authorizeResource: ({ context }) => authorizeIssues(context),
223224
async execute({ input, context, principal }) {
224225
const title = validateTitle(input.title)
225226
if (Buffer.byteLength(input.body, 'utf-8') > MAX_BODY_BYTES)
@@ -283,7 +284,7 @@ export const updateIssue = defineAuthorizedWorkspaceUseCase({
283284
operation: issueOperations.update,
284285
resolveContext: ({ input }: { input: UpdateIssueInput }) => resolveIssueContext({ input }),
285286
authorizationOptions,
286-
authorizeResource: authorizeIssues,
287+
authorizeResource: ({ context }) => authorizeIssues(context),
287288
async execute({ input, context, principal }) {
288289
const current = context.issue
289290
const values: Partial<IssueRow> = {}
@@ -330,7 +331,7 @@ export const startIssue = defineAuthorizedWorkspaceUseCase({
330331
resolveContext: ({ input }: { input: IssueInput & { chatId: string } }) =>
331332
resolveIssueContext({ input }),
332333
authorizationOptions,
333-
authorizeResource: authorizeIssues,
334+
authorizeResource: ({ context }) => authorizeIssues(context),
334335
async execute({ input, context, principal }) {
335336
const userId = requirePrincipalSubjectUserId(principal)
336337
const chat = await getUserWorkspaceChat(db, input.chatId, context.workspaceId, userId)
@@ -363,7 +364,7 @@ export const requestIssueReview = defineAuthorizedWorkspaceUseCase({
363364
resolveContext: ({ input }: { input: IssueInput & { summary: string } }) =>
364365
resolveIssueContext({ input }),
365366
authorizationOptions,
366-
authorizeResource: authorizeIssues,
367+
authorizeResource: ({ context }) => authorizeIssues(context),
367368
async execute({ input, context, principal }) {
368369
const summary = input.summary.trim()
369370
if (!summary) throw new OrchestrationError('validation', 'Say what changed for the reviewer')
@@ -388,7 +389,7 @@ export const approveIssue = defineAuthorizedWorkspaceUseCase({
388389
operation: issueOperations.review,
389390
resolveContext: resolveIssueContext,
390391
authorizationOptions,
391-
authorizeResource: authorizeIssues,
392+
authorizeResource: ({ context }) => authorizeIssues(context),
392393
async execute({ context, principal }) {
393394
await transition(
394395
context.issue.id,
@@ -408,7 +409,7 @@ export const requestIssueChanges = defineAuthorizedWorkspaceUseCase({
408409
resolveContext: ({ input }: { input: IssueInput & { note?: string } }) =>
409410
resolveIssueContext({ input }),
410411
authorizationOptions,
411-
authorizeResource: authorizeIssues,
412+
authorizeResource: ({ context }) => authorizeIssues(context),
412413
async execute({ input, context, principal }) {
413414
const note = input.note?.trim() || null
414415
await transition(
@@ -433,7 +434,7 @@ export const closeIssue = defineAuthorizedWorkspaceUseCase({
433434
operation: issueOperations.close,
434435
resolveContext: ({ input }: { input: CloseIssueInput }) => resolveIssueContext({ input }),
435436
authorizationOptions,
436-
authorizeResource: authorizeIssues,
437+
authorizeResource: ({ context }) => authorizeIssues(context),
437438
async execute({ input, context, principal }) {
438439
let duplicateOf: IssueRow | null = null
439440
if (input.reason === 'duplicate') {
@@ -474,7 +475,7 @@ export const reopenIssue = defineAuthorizedWorkspaceUseCase({
474475
operation: issueOperations.close,
475476
resolveContext: resolveIssueContext,
476477
authorizationOptions,
477-
authorizeResource: authorizeIssues,
478+
authorizeResource: ({ context }) => authorizeIssues(context),
478479
async execute({ context, principal }) {
479480
await transition(
480481
context.issue.id,
@@ -504,7 +505,7 @@ export const addIssueResource = defineAuthorizedWorkspaceUseCase({
504505
operation: issueOperations.update,
505506
resolveContext: ({ input }: { input: IssueResourceInput }) => resolveIssueContext({ input }),
506507
authorizationOptions,
507-
authorizeResource: authorizeIssues,
508+
authorizeResource: ({ context }) => authorizeIssues(context),
508509
async execute({ input, context, principal }) {
509510
await db.transaction(async (tx) => {
510511
const added = await insertIssueResourceInTx(
@@ -528,7 +529,7 @@ export const removeIssueResource = defineAuthorizedWorkspaceUseCase({
528529
operation: issueOperations.update,
529530
resolveContext: ({ input }: { input: IssueResourceInput }) => resolveIssueContext({ input }),
530531
authorizationOptions,
531-
authorizeResource: authorizeIssues,
532+
authorizeResource: ({ context }) => authorizeIssues(context),
532533
async execute({ input, context, principal }) {
533534
await db.transaction(async (tx) => {
534535
const removed = await deleteIssueResourceInTx(
@@ -553,7 +554,7 @@ export const linkIssueTicket = defineAuthorizedWorkspaceUseCase({
553554
resolveContext: ({ input }: { input: IssueInput & { ticket: IssueTicketInsert } }) =>
554555
resolveIssueContext({ input }),
555556
authorizationOptions,
556-
authorizeResource: authorizeIssues,
557+
authorizeResource: ({ context }) => authorizeIssues(context),
557558
async execute({ input, context, principal }) {
558559
await db.transaction(async (tx) => {
559560
const linked = await insertIssueTicketInTx(tx, context.issue.id, input.ticket)
@@ -573,7 +574,7 @@ export const unlinkIssueTicket = defineAuthorizedWorkspaceUseCase({
573574
resolveContext: ({ input }: { input: IssueInput & { ticketId: string } }) =>
574575
resolveIssueContext({ input }),
575576
authorizationOptions,
576-
authorizeResource: authorizeIssues,
577+
authorizeResource: ({ context }) => authorizeIssues(context),
577578
async execute({ input, context, principal }) {
578579
await db.transaction(async (tx) => {
579580
const unlinked = await deleteIssueTicketInTx(tx, context.issue.id, input.ticketId)
@@ -596,7 +597,7 @@ export const addIssueComment = defineAuthorizedWorkspaceUseCase({
596597
resolveContext: ({ input }: { input: IssueInput & { body: string } }) =>
597598
resolveIssueContext({ input }),
598599
authorizationOptions,
599-
authorizeResource: authorizeIssues,
600+
authorizeResource: ({ context }) => authorizeIssues(context),
600601
async execute({ input, context, principal }) {
601602
const body = input.body.trim()
602603
if (!body) throw new OrchestrationError('validation', 'A comment cannot be empty')
@@ -620,7 +621,7 @@ export const deleteIssueComment = defineAuthorizedWorkspaceUseCase({
620621
resolveContext: ({ input }: { input: IssueInput & { commentId: string } }) =>
621622
resolveIssueContext({ input }),
622623
authorizationOptions,
623-
authorizeResource: authorizeIssues,
624+
authorizeResource: ({ context }) => authorizeIssues(context),
624625
async execute({ input, context, principal }) {
625626
const userId = requirePrincipalSubjectUserId(principal)
626627
const deleted = await db.transaction((tx) =>

‎apps/sim/lib/issues/repository.integration.ts‎

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { readFileSync } from 'node:fs'
1+
import { readdirSync, readFileSync } from 'node:fs'
22
import { join } from 'node:path'
3+
import type * as schema from '@sim/db/schema'
34
import { readTestDatabaseUrl } from '@sim/db/testing/test-infrastructure'
45
import { withUtcTimestamps } from '@sim/db/timestamps'
56
import { generateId } from '@sim/utils/id'
67
import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js'
78
import postgres from 'postgres'
89
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
910

10-
const database = vi.hoisted(() => ({ current: undefined as PostgresJsDatabase | undefined }))
11+
const database = vi.hoisted(() => ({
12+
current: undefined as PostgresJsDatabase<typeof schema> | undefined,
13+
}))
1114
vi.mock('@sim/db', () => ({
1215
get db() {
1316
if (!database.current) throw new Error('Test database not initialized')
@@ -17,11 +20,20 @@ vi.mock('@sim/db', () => ({
1720

1821
import { allocateIssueNumber, updateIssueInTx } from '@/lib/issues/repository'
1922

20-
const MIGRATION = join(__dirname, '../../../../packages/db/migrations/0393_issue.sql')
23+
const MIGRATIONS_DIR = join(__dirname, '../../../../packages/db/migrations')
24+
25+
/** Found by name, so renumbering on a rebase never strands the test on a missing file. */
26+
function issueMigrationPath(): string {
27+
const [file, ...rest] = readdirSync(MIGRATIONS_DIR).filter((name) =>
28+
/^\d+_issue\.sql$/.test(name)
29+
)
30+
if (!file || rest.length > 0) throw new Error('Expected exactly one *_issue.sql migration')
31+
return join(MIGRATIONS_DIR, file)
32+
}
2133

2234
/** The migration's own statements for the issue tables, checks, partial indexes and trigger. */
2335
function issueStatements(): string[] {
24-
return readFileSync(MIGRATION, 'utf-8')
36+
return readFileSync(issueMigrationPath(), 'utf-8')
2537
.split('--> statement-breakpoint')
2638
.map((statement) => statement.trim())
2739
.filter(
@@ -45,7 +57,7 @@ describe('issue state in PostgreSQL', () => {
4557
await connection`CREATE TABLE copilot_chats (id uuid PRIMARY KEY)`
4658
for (const statement of issueStatements()) await connection.unsafe(statement)
4759
await connection`ALTER TABLE issue ADD FOREIGN KEY (working_chat_id) REFERENCES copilot_chats(id) ON DELETE SET NULL`
48-
database.current = drizzle(connection)
60+
database.current = drizzle(connection, { schema })
4961
})
5062

5163
afterAll(async () => {
@@ -73,7 +85,8 @@ describe('issue state in PostgreSQL', () => {
7385
}
7486

7587
async function read(id: string) {
76-
const [row] = await connection`SELECT status, working_chat_id FROM issue WHERE id = ${id}`
88+
const [row] = await connection<{ status: string; working_chat_id: string | null }[]>`
89+
SELECT status, working_chat_id FROM issue WHERE id = ${id}`
7790
const events = await connection`SELECT kind, payload FROM issue_event WHERE issue_id = ${id}`
7891
return { ...row, events }
7992
}

‎apps/sim/lib/mothership/chat/post.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ const GENERIC_RESOURCE_TITLE: Record<z.infer<typeof ResourceAttachmentSchema>['t
130130
integration: 'Integration',
131131
file: 'File',
132132
dashboard: 'Dashboard',
133+
issue: 'Issue',
133134
knowledgebase: 'Knowledge Base',
134135
folder: 'Folder',
135136
filefolder: 'File Folder',

0 commit comments

Comments
 (0)