diff --git a/src/app/api/label-mapping/label-mapping.service.test.ts b/src/app/api/label-mapping/label-mapping.service.test.ts
new file mode 100644
index 000000000..24a195ede
--- /dev/null
+++ b/src/app/api/label-mapping/label-mapping.service.test.ts
@@ -0,0 +1,44 @@
+const mockLabelFindFirst = jest.fn()
+const mockLabelDelete = jest.fn()
+
+jest.mock('@/lib/db', () => ({
+ __esModule: true,
+ default: {
+ getInstance: () => ({
+ label: { findFirst: mockLabelFindFirst, delete: mockLabelDelete },
+ }),
+ },
+}))
+
+jest.mock('@/utils/CopilotAPI', () => ({ CopilotAPI: jest.fn() }))
+
+import { LabelMappingService } from '@api/label-mapping/label-mapping.service'
+import User from '@api/core/models/User.model'
+import { UserRole } from '@api/core/types/user'
+
+const user = {
+ workspaceId: 'ws-1',
+ role: UserRole.IU,
+ internalUserId: 'iu-1',
+ token: 'token',
+} as unknown as User
+
+describe('LabelMappingService#deleteLabel', () => {
+ beforeEach(() => jest.clearAllMocks())
+
+ it('deletes the matching label row', async () => {
+ mockLabelFindFirst.mockResolvedValue({ id: 'label-1' })
+
+ await new LabelMappingService(user).deleteLabel('ASS10-009')
+
+ expect(mockLabelDelete).toHaveBeenCalledWith({ where: { id: 'label-1' } })
+ })
+
+ it('no-ops when the label row is already gone', async () => {
+ mockLabelFindFirst.mockResolvedValue(null)
+
+ await new LabelMappingService(user).deleteLabel('ASS10-009')
+
+ expect(mockLabelDelete).not.toHaveBeenCalled()
+ })
+})
diff --git a/src/app/api/label-mapping/label-mapping.service.ts b/src/app/api/label-mapping/label-mapping.service.ts
index 42007a03c..b0d9c2abd 100644
--- a/src/app/api/label-mapping/label-mapping.service.ts
+++ b/src/app/api/label-mapping/label-mapping.service.ts
@@ -175,9 +175,10 @@ export class LabelMappingService extends BaseService {
label,
},
})
+ if (!currentLabel) return
await this.db.label.delete({
where: {
- id: currentLabel?.id,
+ id: currentLabel.id,
},
})
}
diff --git a/src/app/notification-center/page.tsx b/src/app/notification-center/page.tsx
index e39b77851..364d5b8c5 100644
--- a/src/app/notification-center/page.tsx
+++ b/src/app/notification-center/page.tsx
@@ -9,9 +9,9 @@ async function getNotificationDetail(token: string) {
const copilot = new CopilotAPI(token)
const tokenPayload = await copilot.getTokenPayload()
- if (!tokenPayload) throw new Error('Failed to get token payload')
+ if (!tokenPayload?.notificationId) return null
- return await copilot.getIUNotification(z.string().parse(tokenPayload.notificationId), tokenPayload.workspaceId) // notification "id" is expected in tokenPayload
+ return await copilot.getIUNotification(tokenPayload.notificationId, tokenPayload.workspaceId)
}
export default async function NotificationCenter(props: { searchParams: Promise<{ token: string }> }) {
@@ -21,13 +21,22 @@ export default async function NotificationCenter(props: { searchParams: Promise<
return
}
- const notificationDetail = await getNotificationDetail(token)
+ let notificationDetail
+ try {
+ notificationDetail = await getNotificationDetail(token)
+ } catch (error) {
+ console.warn('notification-center: failed to load notification', error)
+ return
+ }
+
if (!notificationDetail) return
- const params = NotificationInProductCtaParamsSchema.parse(notificationDetail.deliveryTargets?.inProduct?.ctaParams)
+ const params = NotificationInProductCtaParamsSchema.safeParse(notificationDetail.deliveryTargets?.inProduct?.ctaParams)
+ if (!params.success) {
+ return
+ }
- redirectIfTaskCta({ ...params, ...searchParams }, UserType.INTERNAL_USER, true)
+ redirectIfTaskCta({ ...params.data, ...searchParams }, UserType.INTERNAL_USER, true)
- // Silent Error is shown if redirect fails. Only possible reason for redirect to not work can be of the taskId not found
return
}