mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
🔀 Merge branch 'feature/jira' of https://github.com/RicardoE105/n8n into RicardoE105-feature/jira
This commit is contained in:
commit
57d296d30a
|
@ -1,4 +1,6 @@
|
|||
import { OptionsWithUri } from 'request';
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
|
@ -41,12 +43,12 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut
|
|||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
let errorMessage = error;
|
||||
if (error.error && error.error.errorMessages) {
|
||||
errorMessage = error.error.errorMessages;
|
||||
}
|
||||
const errorMessage = error.response.body.message || error.response.body.error || error.response.body.errors;
|
||||
if (errorMessage !== undefined) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function jiraSoftwareCloudApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
|
|
@ -44,7 +44,7 @@ export const issueOperations = [
|
|||
description: 'Creates an email notification for an issue and adds it to the mail queue.',
|
||||
},
|
||||
{
|
||||
name: 'Transitions',
|
||||
name: 'Status',
|
||||
value: 'transitions',
|
||||
description: `Returns either all transitions or a transition that can be performed by the user on an issue, based on the issue's status.`,
|
||||
},
|
||||
|
@ -101,6 +101,9 @@ export const issueFields = [
|
|||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getIssueTypes',
|
||||
loadOptionsDependsOn: [
|
||||
'project',
|
||||
],
|
||||
},
|
||||
description: 'Issue Types',
|
||||
},
|
||||
|
@ -139,36 +142,6 @@ export const issueFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Parent Issue Key',
|
||||
name: 'parentIssueKey',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Parent Issue Key',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
},
|
||||
default: [],
|
||||
required : false,
|
||||
description: 'Labels',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getPriorities',
|
||||
},
|
||||
default: '',
|
||||
required : false,
|
||||
description: 'Priority',
|
||||
},
|
||||
{
|
||||
displayName: 'Assignee',
|
||||
name: 'assignee',
|
||||
|
@ -188,6 +161,36 @@ export const issueFields = [
|
|||
required : false,
|
||||
description: 'Description',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
},
|
||||
default: [],
|
||||
required : false,
|
||||
description: 'Labels',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent Issue Key',
|
||||
name: 'parentIssueKey',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Parent Issue Key',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getPriorities',
|
||||
},
|
||||
default: '',
|
||||
required : false,
|
||||
description: 'Priority',
|
||||
},
|
||||
{
|
||||
displayName: 'Update History',
|
||||
name: 'updateHistory',
|
||||
|
@ -238,55 +241,6 @@ export const issueFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Issue Type',
|
||||
name: 'issueType',
|
||||
type: 'options',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getIssueTypes',
|
||||
},
|
||||
default: '',
|
||||
description: 'Issue Types',
|
||||
},
|
||||
{
|
||||
displayName: 'Summary',
|
||||
name: 'summary',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Summary',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent Issue Key',
|
||||
name: 'parentIssueKey',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Parent Issue Key',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
},
|
||||
default: [],
|
||||
required : false,
|
||||
description: 'Labels',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getPriorities',
|
||||
},
|
||||
default: '',
|
||||
required : false,
|
||||
description: 'Priority',
|
||||
},
|
||||
{
|
||||
displayName: 'Assignee',
|
||||
name: 'assignee',
|
||||
|
@ -306,6 +260,63 @@ export const issueFields = [
|
|||
required : false,
|
||||
description: 'Description',
|
||||
},
|
||||
{
|
||||
displayName: 'Issue Type',
|
||||
name: 'issueType',
|
||||
type: 'options',
|
||||
required: false,
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getIssueTypes',
|
||||
},
|
||||
default: '',
|
||||
description: 'Issue Types',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
},
|
||||
default: [],
|
||||
required : false,
|
||||
description: 'Labels',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent Issue Key',
|
||||
name: 'parentIssueKey',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Parent Issue Key',
|
||||
},
|
||||
{
|
||||
displayName: 'Priority',
|
||||
name: 'priority',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getPriorities',
|
||||
},
|
||||
default: '',
|
||||
required : false,
|
||||
description: 'Priority',
|
||||
},
|
||||
{
|
||||
displayName: 'Summary',
|
||||
name: 'summary',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'Summary',
|
||||
},
|
||||
{
|
||||
displayName: 'Status ID',
|
||||
name: 'statusId',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'The ID of the issue status.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
@ -387,6 +398,23 @@ export const issueFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Expand',
|
||||
name: 'expand',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: `Use expand to include additional information about the issues in the response.<br/>
|
||||
This parameter accepts a comma-separated list. Expand options include:<br/>
|
||||
renderedFields Returns field values rendered in HTML format.<br/>
|
||||
names Returns the display name of each field.<br/>
|
||||
schema Returns the schema describing a field type.<br/>
|
||||
transitions Returns all possible transitions for the issue.<br/>
|
||||
editmeta Returns information about how each field can be edited.<br/>
|
||||
changelog Returns a list of recent updates to an issue, sorted by date, starting from the most recent.<br/>
|
||||
versionedRepresentations Returns a JSON array for each version of a field's value, with the highest number<br/>
|
||||
representing the most recent version. Note: When included in the request, the fields parameter is ignored.`
|
||||
},
|
||||
{
|
||||
displayName: 'Fields',
|
||||
name: 'fields',
|
||||
|
@ -410,23 +438,6 @@ export const issueFields = [
|
|||
This parameter is useful where fields have been added by a connect app and a field's key<br/>
|
||||
may differ from its ID.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Expand',
|
||||
name: 'expand',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: '',
|
||||
description: `Use expand to include additional information about the issues in the response.<br/>
|
||||
This parameter accepts a comma-separated list. Expand options include:<br/>
|
||||
renderedFields Returns field values rendered in HTML format.<br/>
|
||||
names Returns the display name of each field.<br/>
|
||||
schema Returns the schema describing a field type.<br/>
|
||||
transitions Returns all possible transitions for the issue.<br/>
|
||||
editmeta Returns information about how each field can be edited.<br/>
|
||||
changelog Returns a list of recent updates to an issue, sorted by date, starting from the most recent.<br/>
|
||||
versionedRepresentations Returns a JSON array for each version of a field's value, with the highest number<br/>
|
||||
representing the most recent version. Note: When included in the request, the fields parameter is ignored.`
|
||||
},
|
||||
{
|
||||
displayName: 'Properties',
|
||||
name: 'properties',
|
||||
|
@ -715,6 +726,17 @@ export const issueFields = [
|
|||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'HTML Body',
|
||||
name: 'htmlBody',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'The HTML body of the email notification for the issue.',
|
||||
},
|
||||
{
|
||||
displayName: 'Subject',
|
||||
name: 'subject',
|
||||
|
@ -736,17 +758,6 @@ export const issueFields = [
|
|||
description: `The subject of the email notification for the issue.
|
||||
If this is not specified, then the subject is set to the issue key and summary.`
|
||||
},
|
||||
{
|
||||
displayName: 'HTML Body',
|
||||
name: 'htmlBody',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
required: false,
|
||||
default: '',
|
||||
description: 'The HTML body of the email notification for the issue.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import { IDataObject } from "n8n-workflow";
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export interface IFields {
|
||||
summary?: string;
|
||||
project?: IDataObject;
|
||||
issuetype?: IDataObject;
|
||||
labels?: string[];
|
||||
priority?: IDataObject;
|
||||
assignee?: IDataObject;
|
||||
description?: string;
|
||||
issuetype?: IDataObject;
|
||||
labels?: string[];
|
||||
parent?: IDataObject;
|
||||
priority?: IDataObject;
|
||||
project?: IDataObject;
|
||||
summary?: string;
|
||||
}
|
||||
|
||||
export interface IIssue {
|
||||
fields?: IFields;
|
||||
transition?: IDataObject;
|
||||
}
|
||||
|
||||
export interface INotify {
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeTypeDescription,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
jiraSoftwareCloudApiRequest,
|
||||
jiraSoftwareCloudApiRequestAllItems,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
issueOperations,
|
||||
issueFields,
|
||||
} from './IssueDescription';
|
||||
|
||||
import {
|
||||
IIssue,
|
||||
IFields,
|
||||
INotify,
|
||||
IIssue,
|
||||
INotificationRecipients,
|
||||
INotify,
|
||||
NotificationRecipientsRestrictions,
|
||||
} from './IssueInterface';
|
||||
|
||||
|
@ -37,7 +41,7 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
description: 'Consume Jira Software API',
|
||||
defaults: {
|
||||
name: 'Jira Software',
|
||||
color: '#c02428',
|
||||
color: '#4185f7',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
|
@ -113,11 +117,8 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
if (jiraCloudCredentials === undefined) {
|
||||
endpoint = '/project';
|
||||
}
|
||||
try {
|
||||
projects = await jiraSoftwareCloudApiRequest.call(this, endpoint, 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
if (projects.values && Array.isArray(projects.values)) {
|
||||
projects = projects.values;
|
||||
}
|
||||
|
@ -135,14 +136,14 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
// Get all the issue types to display them to user so that he can
|
||||
// select them easily
|
||||
async getIssueTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const projectId = this.getCurrentNodeParameter('project');
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let issueTypes;
|
||||
try {
|
||||
|
||||
issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/issuetype', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
for (const issueType of issueTypes) {
|
||||
if (issueType.scope.project.id === projectId) {
|
||||
const issueTypeName = issueType.name;
|
||||
const issueTypeId = issueType.id;
|
||||
|
||||
|
@ -151,6 +152,7 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
value: issueTypeId,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
|
@ -159,11 +161,9 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
async getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let labels;
|
||||
try {
|
||||
|
||||
labels = await jiraSoftwareCloudApiRequest.call(this, '/label', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
for (const label of labels.values) {
|
||||
const labelName = label;
|
||||
const labelId = label;
|
||||
|
@ -181,11 +181,9 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
async getPriorities(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let priorities;
|
||||
try {
|
||||
|
||||
priorities = await jiraSoftwareCloudApiRequest.call(this, '/priority', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
for (const priority of priorities) {
|
||||
const priorityName = priority.name;
|
||||
const priorityId = priority.id;
|
||||
|
@ -203,11 +201,9 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let users;
|
||||
try {
|
||||
|
||||
users = await jiraSoftwareCloudApiRequest.call(this, '/users/search', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
for (const user of users) {
|
||||
const userName = user.displayName;
|
||||
const userId = user.accountId;
|
||||
|
@ -225,11 +221,9 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
async getGroups(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
let groups;
|
||||
try {
|
||||
|
||||
groups = await jiraSoftwareCloudApiRequest.call(this, '/groups/picker', 'GET');
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${err}`);
|
||||
}
|
||||
|
||||
for (const group of groups.groups) {
|
||||
const groupName = group.name;
|
||||
const groupId = group.name;
|
||||
|
@ -309,11 +303,7 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
};
|
||||
}
|
||||
body.fields = fields;
|
||||
try {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, '/issue', 'POST', body);
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-put
|
||||
if (operation === 'update') {
|
||||
|
@ -363,11 +353,13 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
};
|
||||
}
|
||||
body.fields = fields;
|
||||
try {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'PUT', body);
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
|
||||
if (updateFields.statusId) {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/transitions`, 'POST', { transition: { id: updateFields.statusId } });
|
||||
}
|
||||
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'PUT', body);
|
||||
responseData = { success: true };
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-get
|
||||
if (operation === 'get') {
|
||||
|
@ -388,11 +380,9 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
if (additionalFields.updateHistory) {
|
||||
qs.updateHistory = additionalFields.updateHistory as string;
|
||||
}
|
||||
try {
|
||||
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'GET', {}, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
|
||||
if (operation === 'getAll') {
|
||||
|
@ -421,7 +411,6 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
if (operation === 'changelog') {
|
||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
try {
|
||||
if (returnAll) {
|
||||
responseData = await jiraSoftwareCloudApiRequestAllItems.call(this, 'values',`/issue/${issueKey}/changelog`, 'GET');
|
||||
} else {
|
||||
|
@ -429,9 +418,6 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/changelog`, 'GET', {}, qs);
|
||||
responseData = responseData.values;
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-notify-post
|
||||
if (operation === 'notify') {
|
||||
|
@ -513,11 +499,8 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
body.restrict = notificationRecipientsRestrictionsJson;
|
||||
}
|
||||
}
|
||||
try {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/notify`, 'POST', body, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-transitions-get
|
||||
if (operation === 'transitions') {
|
||||
|
@ -532,23 +515,16 @@ export class JiraSoftwareCloud implements INodeType {
|
|||
if (additionalFields.skipRemoteOnlyCondition) {
|
||||
qs.skipRemoteOnlyCondition = additionalFields.skipRemoteOnlyCondition as boolean;
|
||||
}
|
||||
try {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/transitions`, 'GET', {}, qs);
|
||||
responseData = responseData.transitions;
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-delete
|
||||
if (operation === 'delete') {
|
||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||
const deleteSubtasks = this.getNodeParameter('deleteSubtasks', i) as boolean;
|
||||
qs.deleteSubtasks = deleteSubtasks;
|
||||
try {
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'DELETE', {}, qs);
|
||||
} catch (err) {
|
||||
throw new Error(`Jira Error: ${JSON.stringify(err)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(responseData)) {
|
||||
|
|
Loading…
Reference in a new issue