diff --git a/packages/nodes-base/nodes/Jira/GenericFunctions.ts b/packages/nodes-base/nodes/Jira/GenericFunctions.ts
index 66ca437ded..133578fd50 100644
--- a/packages/nodes-base/nodes/Jira/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Jira/GenericFunctions.ts
@@ -1,4 +1,6 @@
-import { OptionsWithUri } from 'request';
+import {
+ OptionsWithUri,
+ } from 'request';
import {
IExecuteFunctions,
@@ -41,11 +43,11 @@ 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 new Error(errorMessage);
+ throw error;
}
}
diff --git a/packages/nodes-base/nodes/Jira/IssueDescription.ts b/packages/nodes-base/nodes/Jira/IssueDescription.ts
index ae46db23e3..74708b088f 100644
--- a/packages/nodes-base/nodes/Jira/IssueDescription.ts
+++ b/packages/nodes-base/nodes/Jira/IssueDescription.ts
@@ -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.
+ This parameter accepts a comma-separated list. Expand options include:
+ renderedFields Returns field values rendered in HTML format.
+ names Returns the display name of each field.
+ schema Returns the schema describing a field type.
+ transitions Returns all possible transitions for the issue.
+ editmeta Returns information about how each field can be edited.
+ changelog Returns a list of recent updates to an issue, sorted by date, starting from the most recent.
+ versionedRepresentations Returns a JSON array for each version of a field's value, with the highest number
+ 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
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.
- This parameter accepts a comma-separated list. Expand options include:
- renderedFields Returns field values rendered in HTML format.
- names Returns the display name of each field.
- schema Returns the schema describing a field type.
- transitions Returns all possible transitions for the issue.
- editmeta Returns information about how each field can be edited.
- changelog Returns a list of recent updates to an issue, sorted by date, starting from the most recent.
- versionedRepresentations Returns a JSON array for each version of a field's value, with the highest number
- 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.',
- },
],
},
{
diff --git a/packages/nodes-base/nodes/Jira/IssueInterface.ts b/packages/nodes-base/nodes/Jira/IssueInterface.ts
index 59ba0ca1e7..fd7a948e29 100644
--- a/packages/nodes-base/nodes/Jira/IssueInterface.ts
+++ b/packages/nodes-base/nodes/Jira/IssueInterface.ts
@@ -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 {
diff --git a/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts b/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts
index b4e3cf0c2a..3388acaeeb 100644
--- a/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts
+++ b/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts
@@ -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}`);
- }
+ projects = await jiraSoftwareCloudApiRequest.call(this, endpoint, 'GET');
+
if (projects.values && Array.isArray(projects.values)) {
projects = projects.values;
}
@@ -135,21 +136,22 @@ 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 {
+ 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) {
- const issueTypeName = issueType.name;
- const issueTypeId = issueType.id;
- returnData.push({
- name: issueTypeName,
- value: issueTypeId,
- });
+ issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/issuetype', 'GET');
+
+ for (const issueType of issueTypes) {
+ if (issueType.scope.project.id === projectId) {
+ const issueTypeName = issueType.name;
+ const issueTypeId = issueType.id;
+
+ returnData.push({
+ name: issueTypeName,
+ value: issueTypeId,
+ });
+ }
}
return returnData;
},
@@ -159,11 +161,9 @@ export class JiraSoftwareCloud implements INodeType {
async getLabels(this: ILoadOptionsFunctions): Promise {
const returnData: INodePropertyOptions[] = [];
let labels;
- try {
- labels = await jiraSoftwareCloudApiRequest.call(this, '/label', 'GET');
- } catch (err) {
- throw new Error(`Jira Error: ${err}`);
- }
+
+ labels = await jiraSoftwareCloudApiRequest.call(this, '/label', 'GET');
+
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 {
const returnData: INodePropertyOptions[] = [];
let priorities;
- try {
- priorities = await jiraSoftwareCloudApiRequest.call(this, '/priority', 'GET');
- } catch (err) {
- throw new Error(`Jira Error: ${err}`);
- }
+
+ priorities = await jiraSoftwareCloudApiRequest.call(this, '/priority', 'GET');
+
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 {
const returnData: INodePropertyOptions[] = [];
let users;
- try {
- users = await jiraSoftwareCloudApiRequest.call(this, '/users/search', 'GET');
- } catch (err) {
- throw new Error(`Jira Error: ${err}`);
- }
+
+ users = await jiraSoftwareCloudApiRequest.call(this, '/users/search', 'GET');
+
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 {
const returnData: INodePropertyOptions[] = [];
let groups;
- try {
- groups = await jiraSoftwareCloudApiRequest.call(this, '/groups/picker', 'GET');
- } catch (err) {
- throw new Error(`Jira Error: ${err}`);
- }
+
+ groups = await jiraSoftwareCloudApiRequest.call(this, '/groups/picker', 'GET');
+
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)}`);
- }
+ responseData = await jiraSoftwareCloudApiRequest.call(this, '/issue', 'POST', body);
}
//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)}`);
- }
+
+ responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'GET', {}, qs);
+
}
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
if (operation === 'getAll') {
@@ -421,16 +411,12 @@ 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 {
- qs.maxResults = this.getNodeParameter('limit', i) as number;
- responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/changelog`, 'GET', {}, qs);
- responseData = responseData.values;
- }
- } catch (err) {
- throw new Error(`Jira Error: ${JSON.stringify(err)}`);
+ if (returnAll) {
+ responseData = await jiraSoftwareCloudApiRequestAllItems.call(this, 'values',`/issue/${issueKey}/changelog`, 'GET');
+ } else {
+ qs.maxResults = this.getNodeParameter('limit', i) as number;
+ responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/changelog`, 'GET', {}, qs);
+ responseData = responseData.values;
}
}
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-notify-post
@@ -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)}`);
- }
+ responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/notify`, 'POST', body, qs);
+
}
//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)}`);
- }
+ responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}/transitions`, 'GET', {}, qs);
+ responseData = responseData.transitions;
+
}
//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)}`);
- }
+ responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'DELETE', {}, qs);
}
}
if (Array.isArray(responseData)) {