fix(GitlabTrigger): use NodeApiError.httpCode and safe description access in webhook checkExists (#35486)

Signed-off-by: sergioperezcheco <checo520@outlook.com>
This commit is contained in:
Checo 2026-10-15 12:53:34 +00:00 committed by GitHub
parent c839d6cd49
commit 054288e488
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 85 additions and 2 deletions

View File

@ -196,7 +196,7 @@ export class GitlabTrigger implements INodeType {
try {
await gitlabApiRequest.call(this, 'GET', endpoint, {});
} catch (error) {
if (error.cause.httpCode === '404' || error.description.includes('404')) {
if (error.httpCode === '404' || error.description?.includes('404')) {
// Webhook does not exist
delete webhookData.webhookId;
delete webhookData.webhookEvents;
@ -205,7 +205,7 @@ export class GitlabTrigger implements INodeType {
return false;
}
// Some error occured
// Some error occurred
throw error;
}

View File

@ -7,7 +7,13 @@ vi.mock('../GitlabTriggerHelpers', () => ({
verifySignature: vi.fn(),
}));
vi.mock('../GenericFunctions', () => ({
gitlabApiRequest: vi.fn(),
}));
import { gitlabApiRequest } from '../GenericFunctions';
import { verifySignature } from '../GitlabTriggerHelpers';
import { NodeApiError } from 'n8n-workflow';
import type { Mock } from 'vitest';
describe('GitlabTrigger', () => {
@ -85,6 +91,83 @@ describe('GitlabTrigger', () => {
});
});
describe('webhookMethods.default.checkExists', () => {
let mockHookFunctions: Partial<IWebhookFunctions>;
const staticData = (webhookId: unknown) => ({
getWorkflowStaticData: vi.fn().mockReturnValue({ webhookId }),
getNodeParameter: vi.fn((name: string) => (name === 'owner' ? 'some-owner' : 'some-repo')),
});
beforeEach(() => {
(gitlabApiRequest as Mock).mockReset();
});
it('should return false when no webhook id is stored', async () => {
mockHookFunctions = staticData(undefined) as unknown as Partial<IWebhookFunctions>;
const result = await trigger.webhookMethods!.default.checkExists.call(
mockHookFunctions as never,
);
expect(result).toBe(false);
expect(gitlabApiRequest).not.toHaveBeenCalled();
});
it('should return true when the API confirms the webhook', async () => {
mockHookFunctions = staticData(42) as unknown as Partial<IWebhookFunctions>;
(gitlabApiRequest as Mock).mockResolvedValue({ id: 42 });
const result = await trigger.webhookMethods!.default.checkExists.call(
mockHookFunctions as never,
);
expect(result).toBe(true);
expect(gitlabApiRequest).toHaveBeenCalledWith(
'GET',
'/projects/some-owner%2Fsome-repo/hooks/42',
{},
);
});
it('should return false and clear static data when the webhook is gone (404 via httpCode)', async () => {
mockHookFunctions = staticData(42) as unknown as Partial<IWebhookFunctions>;
const error = new NodeApiError({} as never, {}, { httpCode: '404' });
(gitlabApiRequest as Mock).mockRejectedValue(error);
const result = await trigger.webhookMethods!.default.checkExists.call(
mockHookFunctions as never,
);
expect(result).toBe(false);
const data = mockHookFunctions.getWorkflowStaticData!('node') as IDataObject;
expect(data.webhookId).toBeUndefined();
expect(data.webhookEvents).toBeUndefined();
expect(data.webhookSecret).toBeUndefined();
});
it('should rethrow the original error when it has no http code and no description', async () => {
mockHookFunctions = staticData(42) as unknown as Partial<IWebhookFunctions>;
const error = new NodeApiError({} as never, {}, { message: 'socket hang up' });
error.description = undefined;
(gitlabApiRequest as Mock).mockRejectedValue(error);
await expect(
trigger.webhookMethods!.default.checkExists.call(mockHookFunctions as never),
).rejects.toBe(error);
});
it('should rethrow non-404 errors', async () => {
mockHookFunctions = staticData(42) as unknown as Partial<IWebhookFunctions>;
const error = new NodeApiError({} as never, {}, { httpCode: '500' });
(gitlabApiRequest as Mock).mockRejectedValue(error);
await expect(
trigger.webhookMethods!.default.checkExists.call(mockHookFunctions as never),
).rejects.toThrow(error);
});
});
describe('description', () => {
it('should have correct node metadata', () => {
expect(trigger.description.displayName).toBe('GitLab Trigger');