From b31832b8e3439598fff277d91444237b0e8a82ba Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:53:00 +0300 Subject: [PATCH 1/4] chore: Replace BreadcrumbLevel and BreadcrumbType enums with String to accept arbitrary SDK values --- package.json | 2 +- src/typeDefs/event.ts | 35 ++++++++--------------------------- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 4572039e..76aa78a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.4.3", + "version": "1.4.4", "main": "index.ts", "license": "BUSL-1.1", "scripts": { diff --git a/src/typeDefs/event.ts b/src/typeDefs/event.ts index c2ac3a2d..c200de96 100644 --- a/src/typeDefs/event.ts +++ b/src/typeDefs/event.ts @@ -116,29 +116,6 @@ type EventUser { photo: String } -""" -Breadcrumb severity level -""" -enum BreadcrumbLevel { - fatal - error - warning - info - debug -} - -""" -Breadcrumb type - controls categorization and UI appearance -""" -enum BreadcrumbType { - default - request - ui - navigation - logic - error -} - """ Single breadcrumb entry - represents an event that occurred before the error """ @@ -149,9 +126,11 @@ type Breadcrumb { timestamp: Float! """ - Type of breadcrumb - controls UI categorization + Type of breadcrumb - controls UI categorization. + Common values: default, request, ui, navigation, logic, error. + Accepts any string since SDK users may send custom types. """ - type: BreadcrumbType + type: String """ Category of the event - more specific than type @@ -164,9 +143,11 @@ type Breadcrumb { message: String """ - Severity level of the breadcrumb + Severity level of the breadcrumb. + Common values: fatal, error, warning, info, debug. + Accepts any string since SDK users may send custom levels. """ - level: BreadcrumbLevel + level: String """ Arbitrary key-value data associated with the breadcrumb From 6fbc666a9d13b6f31f5e77e33261d5883b6f2a5c Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:10:55 +0300 Subject: [PATCH 2/4] fix: tests --- src/integrations/github/__mocks__/service.ts | 5 +++++ src/integrations/github/routes.ts | 8 ++++---- src/resolvers/project.js | 2 +- test/integrations/github-routes.test.ts | 6 +++--- test/resolvers/project.test.ts | 9 +++++++++ 5 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 src/integrations/github/__mocks__/service.ts diff --git a/src/integrations/github/__mocks__/service.ts b/src/integrations/github/__mocks__/service.ts new file mode 100644 index 00000000..04ff8529 --- /dev/null +++ b/src/integrations/github/__mocks__/service.ts @@ -0,0 +1,5 @@ +export const deleteInstallationMock = jest.fn().mockResolvedValue(undefined); + +export const GitHubService = jest.fn().mockImplementation(() => ({ + deleteInstallation: deleteInstallationMock, +})); diff --git a/src/integrations/github/routes.ts b/src/integrations/github/routes.ts index f000ca24..8144ec34 100644 --- a/src/integrations/github/routes.ts +++ b/src/integrations/github/routes.ts @@ -522,15 +522,15 @@ export function createGitHubRouter(factories: ContextFactories): express.Router * just log query parameters and respond with 200 without signature validation. */ if (req.method !== 'POST') { + // eslint-disable-next-line @typescript-eslint/camelcase, camelcase const { code, installation_id, setup_action, state, ...restQuery } = req.query as Record; + // eslint-disable-next-line @typescript-eslint/camelcase, camelcase if (code || installation_id || state || setup_action) { - // eslint-disable-next-line @typescript-eslint/camelcase, camelcase log('info', `${WEBHOOK_LOG_PREFIX}Received non-POST request on /webhook with OAuth-like params`, { - // eslint-disable-next-line @typescript-eslint/camelcase, camelcase code, - installation_id, - setup_action, + installation_id, // eslint-disable-line @typescript-eslint/camelcase, camelcase + setup_action, // eslint-disable-line @typescript-eslint/camelcase, camelcase state, query: restQuery, }); diff --git a/src/resolvers/project.js b/src/resolvers/project.js index aa16734e..c1a2767c 100644 --- a/src/resolvers/project.js +++ b/src/resolvers/project.js @@ -421,7 +421,7 @@ module.exports = { */ const taskManager = project.taskManager; - if (taskManager && taskManager.type === 'github' && taskManager.config?.installationId) { + if (taskManager && taskManager.type === 'github' && taskManager.config && taskManager.config.installationId) { const githubService = new GitHubService(); await githubService.deleteInstallation(taskManager.config.installationId); diff --git a/test/integrations/github-routes.test.ts b/test/integrations/github-routes.test.ts index 81094183..03eacc94 100644 --- a/test/integrations/github-routes.test.ts +++ b/test/integrations/github-routes.test.ts @@ -480,7 +480,7 @@ describe('GitHub Routes - /integration/github/connect', () => { expect(response.status).toBe(302); expect(response.body).toContain('http://localhost:8080/'); - expect(response.body).toContain('error=Missing+or+invalid+OAuth+code'); + expect(response.body).toContain('apiError=Missing+or+invalid+OAuth+code'); }); it('should redirect with error when state is missing', async () => { @@ -501,7 +501,7 @@ describe('GitHub Routes - /integration/github/connect', () => { expect(response.status).toBe(302); expect(response.body).toContain('http://localhost:8080/'); - expect(response.body).toContain('error=Missing+or+invalid+state'); + expect(response.body).toContain('apiError=Missing+or+invalid+state'); }); it('should redirect with error when state is invalid or expired', async () => { @@ -525,7 +525,7 @@ describe('GitHub Routes - /integration/github/connect', () => { expect(response.status).toBe(302); expect(response.body).toContain('http://localhost:8080/'); - expect(response.body).toContain('error=Invalid+or+expired+state'); + expect(response.body).toContain('apiError=Invalid+or+expired+state'); expect(mockGetState).toHaveBeenCalledWith(state); }); diff --git a/test/resolvers/project.test.ts b/test/resolvers/project.test.ts index bda54a40..9e763bce 100644 --- a/test/resolvers/project.test.ts +++ b/test/resolvers/project.test.ts @@ -3,6 +3,11 @@ import { ObjectId } from 'mongodb'; import { ProjectDBScheme, ProjectTaskManagerConfig } from '@hawk.so/types'; import { ResolverContextWithUser } from '../../src/types/graphql'; import { ApolloError, UserInputError } from 'apollo-server-express'; + +jest.mock('../../src/integrations/github/service'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { deleteInstallationMock, GitHubService } = require('../../src/integrations/github/service'); + // @ts-expect-error - CommonJS module, TypeScript can't infer types properly import projectResolverModule from '../../src/resolvers/project'; @@ -141,6 +146,8 @@ describe('Project Resolver - Task Manager Mutations', () => { )) as { taskManager: ProjectTaskManagerConfig | null }; expect(context.factories.projectsFactory.findById).toHaveBeenCalledWith(mockProject._id.toString()); + expect(GitHubService).toHaveBeenCalledTimes(1); + expect(deleteInstallationMock).toHaveBeenCalledWith('123456'); expect(mockProject.updateProject).toHaveBeenCalledWith({ taskManager: null, }); @@ -217,6 +224,8 @@ describe('Project Resolver - Task Manager Mutations', () => { context )) as { taskManager: ProjectTaskManagerConfig | null }; + expect(GitHubService).not.toHaveBeenCalled(); + expect(deleteInstallationMock).not.toHaveBeenCalled(); expect(mockProject.updateProject).toHaveBeenCalledWith({ taskManager: null, }); From c56ad1b5fca791d025b4443521c8bced08f265b1 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:14:02 +0300 Subject: [PATCH 3/4] refactor: remove GitHub service mock --- .../__mocks__/service.ts => test/__mocks__/github-service.ts | 0 test/resolvers/project.test.ts | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/integrations/github/__mocks__/service.ts => test/__mocks__/github-service.ts (100%) diff --git a/src/integrations/github/__mocks__/service.ts b/test/__mocks__/github-service.ts similarity index 100% rename from src/integrations/github/__mocks__/service.ts rename to test/__mocks__/github-service.ts diff --git a/test/resolvers/project.test.ts b/test/resolvers/project.test.ts index 9e763bce..4a46fd0d 100644 --- a/test/resolvers/project.test.ts +++ b/test/resolvers/project.test.ts @@ -4,9 +4,9 @@ import { ProjectDBScheme, ProjectTaskManagerConfig } from '@hawk.so/types'; import { ResolverContextWithUser } from '../../src/types/graphql'; import { ApolloError, UserInputError } from 'apollo-server-express'; -jest.mock('../../src/integrations/github/service'); +jest.mock('../../src/integrations/github/service', () => require('../__mocks__/github-service')); // eslint-disable-next-line @typescript-eslint/no-var-requires -const { deleteInstallationMock, GitHubService } = require('../../src/integrations/github/service'); +const { deleteInstallationMock, GitHubService } = require('../__mocks__/github-service'); // @ts-expect-error - CommonJS module, TypeScript can't infer types properly import projectResolverModule from '../../src/resolvers/project'; From be71f5d70323ed9cb6f50c1cd4abbea62cd97cf0 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:21:44 +0300 Subject: [PATCH 4/4] fix: import --- test/resolvers/project.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/resolvers/project.test.ts b/test/resolvers/project.test.ts index 4a46fd0d..8f50acbc 100644 --- a/test/resolvers/project.test.ts +++ b/test/resolvers/project.test.ts @@ -6,7 +6,7 @@ import { ApolloError, UserInputError } from 'apollo-server-express'; jest.mock('../../src/integrations/github/service', () => require('../__mocks__/github-service')); // eslint-disable-next-line @typescript-eslint/no-var-requires -const { deleteInstallationMock, GitHubService } = require('../__mocks__/github-service'); +import { deleteInstallationMock, GitHubService } from '../__mocks__/github-service'; // @ts-expect-error - CommonJS module, TypeScript can't infer types properly import projectResolverModule from '../../src/resolvers/project';