mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-27 23:07:12 +02:00
feat(Github Node): Introduce get issues operation on user resource (#16951)
This commit is contained in:
parent
316dafafc0
commit
065bbcfdc7
|
|
@ -324,6 +324,12 @@ export class Github implements INodeType {
|
|||
description: 'Returns the repositories of a user',
|
||||
action: "Get a user's repositories",
|
||||
},
|
||||
{
|
||||
name: 'Get Issues',
|
||||
value: 'getIssues',
|
||||
description: 'Returns the issues assigned to the user',
|
||||
action: "Get a user's issues",
|
||||
},
|
||||
{
|
||||
name: 'Invite',
|
||||
value: 'invite',
|
||||
|
|
@ -548,7 +554,8 @@ export class Github implements INodeType {
|
|||
],
|
||||
displayOptions: {
|
||||
hide: {
|
||||
operation: ['invite'],
|
||||
resource: ['user'],
|
||||
operation: ['invite', 'getIssues'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -2093,6 +2100,147 @@ export class Github implements INodeType {
|
|||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// user:getIssues
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getIssues'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getIssues'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'getUserIssuesFilters',
|
||||
type: 'collection',
|
||||
typeOptions: {
|
||||
multipleValueButtonText: 'Add Filter',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['user'],
|
||||
operation: ['getIssues'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Mentioned',
|
||||
name: 'mentioned',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Return only issues in which a specific user was mentioned',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Return only issues with the given labels. Multiple labels can be separated by comma.',
|
||||
},
|
||||
{
|
||||
displayName: 'Updated Since',
|
||||
name: 'since',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Return only issues updated at or after this time',
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'All',
|
||||
value: 'all',
|
||||
description: 'Returns issues with any state',
|
||||
},
|
||||
{
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
description: 'Return issues with "closed" state',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
description: 'Return issues with "open" state',
|
||||
},
|
||||
],
|
||||
default: 'open',
|
||||
description: 'The state to set',
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Created',
|
||||
value: 'created',
|
||||
description: 'Sort by created date',
|
||||
},
|
||||
{
|
||||
name: 'Updated',
|
||||
value: 'updated',
|
||||
description: 'Sort by updated date',
|
||||
},
|
||||
{
|
||||
name: 'Comments',
|
||||
value: 'comments',
|
||||
description: 'Sort by comments',
|
||||
},
|
||||
],
|
||||
default: 'created',
|
||||
description: 'The order the issues should be returned in',
|
||||
},
|
||||
{
|
||||
displayName: 'Direction',
|
||||
name: 'direction',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascending',
|
||||
value: 'asc',
|
||||
description: 'Sort in ascending order',
|
||||
},
|
||||
{
|
||||
name: 'Descending',
|
||||
value: 'desc',
|
||||
description: 'Sort in descending order',
|
||||
},
|
||||
],
|
||||
default: 'desc',
|
||||
description: 'The sort order',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -2158,6 +2306,7 @@ export class Github implements INodeType {
|
|||
'repository:listPopularPaths',
|
||||
'repository:listReferrers',
|
||||
'user:getRepositories',
|
||||
'user:getIssues',
|
||||
'release:getAll',
|
||||
'review:getAll',
|
||||
'organization:getRepositories',
|
||||
|
|
@ -2231,7 +2380,7 @@ export class Github implements INodeType {
|
|||
qs = {};
|
||||
|
||||
let owner = '';
|
||||
if (fullOperation !== 'user:invite') {
|
||||
if (fullOperation !== 'user:invite' && fullOperation !== 'user:getIssues') {
|
||||
// Request the parameters which almost all operations need
|
||||
owner = this.getNodeParameter('owner', i, '', { extractValue: true }) as string;
|
||||
}
|
||||
|
|
@ -2239,6 +2388,7 @@ export class Github implements INodeType {
|
|||
let repository = '';
|
||||
if (
|
||||
fullOperation !== 'user:getRepositories' &&
|
||||
fullOperation !== 'user:getIssues' &&
|
||||
fullOperation !== 'user:invite' &&
|
||||
fullOperation !== 'organization:getRepositories'
|
||||
) {
|
||||
|
|
@ -2629,6 +2779,20 @@ export class Github implements INodeType {
|
|||
|
||||
returnAll = this.getNodeParameter('returnAll', 0);
|
||||
|
||||
if (!returnAll) {
|
||||
qs.per_page = this.getNodeParameter('limit', 0);
|
||||
}
|
||||
} else if (operation === 'getIssues') {
|
||||
// ----------------------------------
|
||||
// getIssues
|
||||
// ----------------------------------
|
||||
requestMethod = 'GET';
|
||||
|
||||
endpoint = '/issues';
|
||||
|
||||
qs = this.getNodeParameter('getUserIssuesFilters', i, {}) as IDataObject;
|
||||
|
||||
returnAll = this.getNodeParameter('returnAll', 0);
|
||||
if (!returnAll) {
|
||||
qs.per_page = this.getNodeParameter('limit', 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -594,4 +594,109 @@ describe('Test Github Node', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('User Operations', () => {
|
||||
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: {} }]),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should fetch open issues by default (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return {};
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [
|
||||
{ id: 1, title: 'Issue 1', state: 'open' },
|
||||
{ id: 2, title: 'Issue 2', state: 'open' },
|
||||
],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.not.objectContaining({ state: 'closed' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch closed issues when state filter is set to closed (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return { state: 'closed' };
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [{ id: 3, title: 'Issue 3', state: 'closed' }],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.objectContaining({ state: 'closed' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch issues with a specific label (user:getIssues)', async () => {
|
||||
mockExecutionContext.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'resource') return 'user';
|
||||
if (parameterName === 'operation') return 'getIssues';
|
||||
if (parameterName === 'getUserIssuesFilters') return { labels: 'bug' };
|
||||
if (parameterName === 'returnAll') return true;
|
||||
if (parameterName === 'authentication') return 'accessToken';
|
||||
return '';
|
||||
});
|
||||
|
||||
mockExecutionContext.helpers.requestWithAuthentication.mockResolvedValue({
|
||||
body: [{ id: 4, title: 'Issue 4', state: 'open', labels: ['bug'] }],
|
||||
headers: {},
|
||||
});
|
||||
|
||||
await githubNode.execute.call(mockExecutionContext);
|
||||
expect(mockExecutionContext.helpers.requestWithAuthentication).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
uri: 'https://api.github.com/issues',
|
||||
qs: expect.objectContaining({ labels: 'bug' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user