mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(GitHub Node): Tolerate trailing slash in file path (#15517)
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
import { getRefs, getRepositories, getUsers, getWorkflows } from './SearchFunctions';
|
||||
import { removeTrailingSlash } from '../../utils/utilities';
|
||||
import { defaultWebhookDescription } from '../Webhook/description';
|
||||
|
||||
export class Github implements INodeType {
|
||||
@@ -2250,7 +2251,7 @@ export class Github implements INodeType {
|
||||
|
||||
requestMethod = 'PUT';
|
||||
|
||||
const filePath = this.getNodeParameter('filePath', i);
|
||||
const filePath = removeTrailingSlash(this.getNodeParameter('filePath', i));
|
||||
|
||||
const additionalParameters = this.getNodeParameter(
|
||||
'additionalParameters',
|
||||
@@ -2326,7 +2327,7 @@ export class Github implements INodeType {
|
||||
body.branch = (additionalParameters.branch as IDataObject).branch;
|
||||
}
|
||||
|
||||
const filePath = this.getNodeParameter('filePath', i);
|
||||
const filePath = removeTrailingSlash(this.getNodeParameter('filePath', i));
|
||||
body.message = this.getNodeParameter('commitMessage', i) as string;
|
||||
|
||||
body.sha = await getFileSha.call(
|
||||
@@ -2341,7 +2342,7 @@ export class Github implements INodeType {
|
||||
} else if (operation === 'get') {
|
||||
requestMethod = 'GET';
|
||||
|
||||
const filePath = this.getNodeParameter('filePath', i);
|
||||
const filePath = removeTrailingSlash(this.getNodeParameter('filePath', i));
|
||||
const additionalParameters = this.getNodeParameter(
|
||||
'additionalParameters',
|
||||
i,
|
||||
@@ -2354,7 +2355,7 @@ export class Github implements INodeType {
|
||||
endpoint = `/repos/${owner}/${repository}/contents/${encodeURIComponent(filePath)}`;
|
||||
} else if (operation === 'list') {
|
||||
requestMethod = 'GET';
|
||||
const filePath = this.getNodeParameter('filePath', i);
|
||||
const filePath = removeTrailingSlash(this.getNodeParameter('filePath', i));
|
||||
endpoint = `/repos/${owner}/${repository}/contents/${encodeURIComponent(filePath)}`;
|
||||
}
|
||||
} else if (resource === 'issue') {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
|
||||
import nock from 'nock';
|
||||
|
||||
import * as utilities from '../../../../utils/utilities';
|
||||
import { Github } from '../../Github.node';
|
||||
|
||||
describe('Test Github Node', () => {
|
||||
@@ -62,6 +63,73 @@ describe('Test Github Node', () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
});
|
||||
|
||||
describe('removeTrailingSlash Function', () => {
|
||||
let githubNode: Github;
|
||||
let mockExecutionContext: any;
|
||||
|
||||
beforeEach(() => {
|
||||
githubNode = new Github();
|
||||
mockExecutionContext = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Github' }),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
server: 'https://api.github.com',
|
||||
user: 'test',
|
||||
accessToken: 'test',
|
||||
}),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
requestWithAuthentication: jest.fn().mockResolvedValue({}),
|
||||
constructExecutionMetaData: jest.fn().mockReturnValue([{ json: {} }]),
|
||||
},
|
||||
};
|
||||
|
||||
jest.spyOn(utilities, 'removeTrailingSlash');
|
||||
jest.mock('../../../../utils/utilities', () => ({
|
||||
...jest.requireActual('../../../../utils/utilities'),
|
||||
getFileSha: jest.fn().mockResolvedValue('mockedSHA'),
|
||||
}));
|
||||
});
|
||||
|
||||
it('should call remove trailing slash', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'operation') {
|
||||
return 'list';
|
||||
}
|
||||
if (parameterName === 'resource') {
|
||||
return 'file';
|
||||
}
|
||||
if (parameterName === 'filePath') {
|
||||
return 'path/to/file/';
|
||||
}
|
||||
if (parameterName === 'owner') {
|
||||
return 'me';
|
||||
}
|
||||
if (parameterName === 'repository') {
|
||||
return 'repo';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
|
||||
expect(utilities.removeTrailingSlash).toHaveBeenCalledWith('path/to/file/');
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
'githubOAuth2Api',
|
||||
{
|
||||
body: {},
|
||||
headers: { 'User-Agent': 'n8n' },
|
||||
json: true,
|
||||
method: 'GET',
|
||||
qs: {},
|
||||
uri: 'https://api.github.com/repos/me/repo/contents/path%2Fto%2Ffile',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const baseUrl = 'https://api.github.com';
|
||||
nock(baseUrl)
|
||||
|
||||
@@ -11,12 +11,7 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
export const removeTrailingSlash = (url: string) => {
|
||||
if (url.endsWith('/')) {
|
||||
return url.slice(0, -1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
import { removeTrailingSlash } from '../../utils/utilities';
|
||||
|
||||
export async function strapiApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions | IWebhookFunctions,
|
||||
|
||||
@@ -14,11 +14,11 @@ import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
|
||||
import { entryFields, entryOperations } from './EntryDescription';
|
||||
import {
|
||||
getToken,
|
||||
removeTrailingSlash,
|
||||
strapiApiRequest,
|
||||
strapiApiRequestAllItems,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
import { removeTrailingSlash } from '../../utils/utilities';
|
||||
|
||||
export class Strapi implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
fuzzyCompare,
|
||||
getResolvables,
|
||||
keysToLowercase,
|
||||
removeTrailingSlash,
|
||||
shuffleArray,
|
||||
sortItemKeysByPriorityList,
|
||||
wrapData,
|
||||
@@ -312,3 +313,13 @@ describe('sortItemKeysByPriorityList', () => {
|
||||
expect(Object.keys(result[0].json)).toEqual(['a', 'b', 'd']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeTrailingSlash', () => {
|
||||
it('removes trailing slash', () => {
|
||||
expect(removeTrailingSlash('https://example.com/')).toBe('https://example.com');
|
||||
});
|
||||
|
||||
it('does not change a URL without trailing slash', () => {
|
||||
expect(removeTrailingSlash('https://example.com')).toBe('https://example.com');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -472,3 +472,10 @@ export function createUtmCampaignLink(nodeType: string, instanceId?: string) {
|
||||
nodeType,
|
||||
)}${instanceId ? '_' + instanceId : ''}`;
|
||||
}
|
||||
|
||||
export const removeTrailingSlash = (url: string) => {
|
||||
if (url.endsWith('/')) {
|
||||
return url.slice(0, -1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user