mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
fix(Github Node): Modify regex validation to support custom urls (#19076)
This commit is contained in:
@@ -517,13 +517,13 @@ export class Github implements INodeType {
|
|||||||
placeholder: 'e.g. https://github.com/n8n-io',
|
placeholder: 'e.g. https://github.com/n8n-io',
|
||||||
extractValue: {
|
extractValue: {
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)',
|
regex: 'https:\\/\\/(?:[^/]+)\\/([-_0-9a-zA-Z]+)',
|
||||||
},
|
},
|
||||||
validation: [
|
validation: [
|
||||||
{
|
{
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
properties: {
|
properties: {
|
||||||
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)(?:.*)',
|
regex: 'https:\\/\\/([^/]+)\\/([-_0-9a-zA-Z]+)(?:.*)',
|
||||||
errorMessage: 'Not a valid Github URL',
|
errorMessage: 'Not a valid Github URL',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -579,13 +579,13 @@ export class Github implements INodeType {
|
|||||||
placeholder: 'e.g. https://github.com/n8n-io/n8n',
|
placeholder: 'e.g. https://github.com/n8n-io/n8n',
|
||||||
extractValue: {
|
extractValue: {
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)',
|
regex: 'https:\\/\\/(?:[^/]+)\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)',
|
||||||
},
|
},
|
||||||
validation: [
|
validation: [
|
||||||
{
|
{
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
properties: {
|
properties: {
|
||||||
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)',
|
regex: 'https:\\/\\/([^/]+)\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)',
|
||||||
errorMessage: 'Not a valid Github Repository URL',
|
errorMessage: 'Not a valid Github Repository URL',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -104,13 +104,13 @@ export class GithubTrigger implements INodeType {
|
|||||||
placeholder: 'e.g. https://github.com/n8n-io',
|
placeholder: 'e.g. https://github.com/n8n-io',
|
||||||
extractValue: {
|
extractValue: {
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)',
|
regex: 'https:\\/\\/(?:[^/]+)\\/([-_0-9a-zA-Z]+)',
|
||||||
},
|
},
|
||||||
validation: [
|
validation: [
|
||||||
{
|
{
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
properties: {
|
properties: {
|
||||||
regex: 'https:\\/\\/github.com\\/([-_0-9a-zA-Z]+)(?:.*)',
|
regex: 'https:\\/\\/([^/]+)\\/([-_0-9a-zA-Z]+)(?:.*)',
|
||||||
errorMessage: 'Not a valid Github URL',
|
errorMessage: 'Not a valid Github URL',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -158,13 +158,13 @@ export class GithubTrigger implements INodeType {
|
|||||||
placeholder: 'e.g. https://github.com/n8n-io/n8n',
|
placeholder: 'e.g. https://github.com/n8n-io/n8n',
|
||||||
extractValue: {
|
extractValue: {
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)',
|
regex: 'https:\\/\\/(?:[^/]+)\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)',
|
||||||
},
|
},
|
||||||
validation: [
|
validation: [
|
||||||
{
|
{
|
||||||
type: 'regex',
|
type: 'regex',
|
||||||
properties: {
|
properties: {
|
||||||
regex: 'https:\\/\\/github.com\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)',
|
regex: 'https:\\/\\/([^/]+)\\/(?:[-_0-9a-zA-Z]+)\\/([-_.0-9a-zA-Z]+)(?:.*)',
|
||||||
errorMessage: 'Not a valid Github Repository URL',
|
errorMessage: 'Not a valid Github Repository URL',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
257
packages/nodes-base/nodes/Github/__tests__/UrlPatterns.test.ts
Normal file
257
packages/nodes-base/nodes/Github/__tests__/UrlPatterns.test.ts
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
import { Github } from '../Github.node';
|
||||||
|
import { GithubTrigger } from '../GithubTrigger.node';
|
||||||
|
|
||||||
|
interface ValidationRule {
|
||||||
|
type: string;
|
||||||
|
properties: {
|
||||||
|
regex: string;
|
||||||
|
errorMessage: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('GitHub Node URL Pattern Tests', () => {
|
||||||
|
let githubNode: Github;
|
||||||
|
let githubTriggerNode: GithubTrigger;
|
||||||
|
|
||||||
|
const getOwnerUrlMode = () => {
|
||||||
|
const ownerParam = githubNode.description.properties.find((prop) => prop.name === 'owner');
|
||||||
|
return ownerParam?.modes?.find((mode) => mode.name === 'url');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRepositoryUrlMode = () => {
|
||||||
|
const repoParam = githubNode.description.properties.find((prop) => prop.name === 'repository');
|
||||||
|
return repoParam?.modes?.find((mode) => mode.name === 'url');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOwnerExtractRegex = () => {
|
||||||
|
const mode = getOwnerUrlMode();
|
||||||
|
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOwnerValidationRegex = () => {
|
||||||
|
const mode = getOwnerUrlMode();
|
||||||
|
const validation = mode?.validation?.[0] as ValidationRule;
|
||||||
|
return new RegExp(validation?.properties?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRepositoryExtractRegex = () => {
|
||||||
|
const mode = getRepositoryUrlMode();
|
||||||
|
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRepositoryValidationRegex = () => {
|
||||||
|
const mode = getRepositoryUrlMode();
|
||||||
|
const validation = mode?.validation?.[0] as ValidationRule;
|
||||||
|
return new RegExp(validation?.properties?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper functions for GithubTrigger node
|
||||||
|
const getTriggerOwnerUrlMode = () => {
|
||||||
|
const ownerParam = githubTriggerNode.description.properties.find(
|
||||||
|
(prop) => prop.name === 'owner',
|
||||||
|
);
|
||||||
|
return ownerParam?.modes?.find((mode) => mode.name === 'url');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerRepositoryUrlMode = () => {
|
||||||
|
const repoParam = githubTriggerNode.description.properties.find(
|
||||||
|
(prop) => prop.name === 'repository',
|
||||||
|
);
|
||||||
|
return repoParam?.modes?.find((mode) => mode.name === 'url');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerOwnerExtractRegex = () => {
|
||||||
|
const mode = getTriggerOwnerUrlMode();
|
||||||
|
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerOwnerValidationRegex = () => {
|
||||||
|
const mode = getTriggerOwnerUrlMode();
|
||||||
|
const validation = mode?.validation?.[0] as ValidationRule;
|
||||||
|
return new RegExp(validation?.properties?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerRepositoryExtractRegex = () => {
|
||||||
|
const mode = getTriggerRepositoryUrlMode();
|
||||||
|
return new RegExp(mode?.extractValue?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerRepositoryValidationRegex = () => {
|
||||||
|
const mode = getTriggerRepositoryUrlMode();
|
||||||
|
const validation = mode?.validation?.[0] as ValidationRule;
|
||||||
|
return new RegExp(validation?.properties?.regex ?? '');
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
githubNode = new Github();
|
||||||
|
githubTriggerNode = new GithubTrigger();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GitHub Node Resource Locator Patterns', () => {
|
||||||
|
describe('Owner URL Pattern', () => {
|
||||||
|
it('should extract owner from github.com URL', () => {
|
||||||
|
const regex = getOwnerExtractRegex();
|
||||||
|
const url = 'https://github.com/n8n-io';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('n8n-io');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract owner from custom GitHub URL', () => {
|
||||||
|
const regex = getOwnerExtractRegex();
|
||||||
|
const url = 'https://github.company.com/acme-corp';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('acme-corp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate github.com URL', () => {
|
||||||
|
const validationRegex = getOwnerValidationRegex();
|
||||||
|
const url = 'https://github.com/n8n-io';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate custom GitHub URL', () => {
|
||||||
|
const validationRegex = getOwnerValidationRegex();
|
||||||
|
const url = 'https://github.company.com/acme-corp';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid URLs', () => {
|
||||||
|
const validationRegex = getOwnerValidationRegex();
|
||||||
|
expect(validationRegex.test('not-a-url')).toBe(false);
|
||||||
|
expect(validationRegex.test('http://github.com/user')).toBe(false);
|
||||||
|
expect(validationRegex.test('https://')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Repository URL Pattern', () => {
|
||||||
|
it('should extract repository from github.com URL', () => {
|
||||||
|
const regex = getRepositoryExtractRegex();
|
||||||
|
const url = 'https://github.com/n8n-io/n8n';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('n8n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract repository from custom GitHub URL', () => {
|
||||||
|
const regex = getRepositoryExtractRegex();
|
||||||
|
const url = 'https://github.company.com/acme-corp/my-repo';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('my-repo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate github.com repository URL', () => {
|
||||||
|
const validationRegex = getRepositoryValidationRegex();
|
||||||
|
const url = 'https://github.com/n8n-io/n8n';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate custom GitHub repository URL', () => {
|
||||||
|
const validationRegex = getRepositoryValidationRegex();
|
||||||
|
const url = 'https://github.company.com/acme-corp/my-repo';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate URLs with additional paths', () => {
|
||||||
|
const validationRegex = getRepositoryValidationRegex();
|
||||||
|
expect(validationRegex.test('https://github.com/n8n-io/n8n/issues/123')).toBe(true);
|
||||||
|
expect(validationRegex.test('https://github.company.com/org/repo/pulls')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject invalid repository URLs', () => {
|
||||||
|
const validationRegex = getRepositoryValidationRegex();
|
||||||
|
expect(validationRegex.test('https://github.com/user')).toBe(false);
|
||||||
|
expect(validationRegex.test('not-a-url')).toBe(false);
|
||||||
|
expect(validationRegex.test('https://')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GitHub Trigger Node Resource Locator Patterns', () => {
|
||||||
|
describe('Owner URL Pattern', () => {
|
||||||
|
it('should extract owner from github.com URL', () => {
|
||||||
|
const regex = getTriggerOwnerExtractRegex();
|
||||||
|
const url = 'https://github.com/n8n-io';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('n8n-io');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract owner from custom GitHub URL', () => {
|
||||||
|
const regex = getTriggerOwnerExtractRegex();
|
||||||
|
const url = 'https://github.company.com/my-org';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('my-org');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate github.com URL', () => {
|
||||||
|
const validationRegex = getTriggerOwnerValidationRegex();
|
||||||
|
const url = 'https://github.com/n8n-io';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate custom GitHub URL', () => {
|
||||||
|
const validationRegex = getTriggerOwnerValidationRegex();
|
||||||
|
const url = 'https://github.company.com/my-org';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Repository URL Pattern', () => {
|
||||||
|
it('should extract repository from github.com URL', () => {
|
||||||
|
const regex = getTriggerRepositoryExtractRegex();
|
||||||
|
const url = 'https://github.com/n8n-io/n8n';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('n8n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract repository from custom GitHub URL', () => {
|
||||||
|
const regex = getTriggerRepositoryExtractRegex();
|
||||||
|
const url = 'https://github.company.com/my-org/my-repo';
|
||||||
|
const match = url.match(regex);
|
||||||
|
expect(match?.[1]).toBe('my-repo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate github.com repository URL', () => {
|
||||||
|
const validationRegex = getTriggerRepositoryValidationRegex();
|
||||||
|
const url = 'https://github.com/n8n-io/n8n';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should validate custom GitHub repository URL', () => {
|
||||||
|
const validationRegex = getTriggerRepositoryValidationRegex();
|
||||||
|
const url = 'https://github.company.com/my-org/my-repo';
|
||||||
|
expect(validationRegex.test(url)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('URL Pattern Edge Cases', () => {
|
||||||
|
it('should handle URLs with subdomains', () => {
|
||||||
|
const ownerRegex = getOwnerExtractRegex();
|
||||||
|
const repoRegex = getRepositoryExtractRegex();
|
||||||
|
|
||||||
|
// Test complex custom URLs
|
||||||
|
expect('https://git.internal.company.com/dev-team'.match(ownerRegex)?.[1]).toBe('dev-team');
|
||||||
|
expect('https://github.acme.corp/engineering/backend-api'.match(repoRegex)?.[1]).toBe(
|
||||||
|
'backend-api',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle URLs with ports', () => {
|
||||||
|
const ownerRegex = getOwnerExtractRegex();
|
||||||
|
const repoRegex = getRepositoryExtractRegex();
|
||||||
|
|
||||||
|
// Test URLs with ports
|
||||||
|
expect('https://github.local:8080/testuser'.match(ownerRegex)?.[1]).toBe('testuser');
|
||||||
|
expect('https://git.company.com:443/org/project'.match(repoRegex)?.[1]).toBe('project');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle URLs with additional path segments', () => {
|
||||||
|
const ownerValidationRegex = getOwnerValidationRegex();
|
||||||
|
const repoValidationRegex = getRepositoryValidationRegex();
|
||||||
|
|
||||||
|
// Test URLs with extra paths
|
||||||
|
expect(ownerValidationRegex.test('https://github.com/user/settings')).toBe(true);
|
||||||
|
expect(repoValidationRegex.test('https://github.com/user/repo/issues/123')).toBe(true);
|
||||||
|
expect(repoValidationRegex.test('https://git.company.com/org/project/pulls')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user