🔀 Merge branch 'RicardoE105-jira-node'

This commit is contained in:
Jan Oberhauser 2019-12-02 22:41:06 +01:00
commit 81b17d43ed
7 changed files with 1506 additions and 1 deletions

View file

@ -0,0 +1,29 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class JiraSoftwareCloudApi implements ICredentialType {
name = 'jiraSoftwareCloudApi';
displayName = 'Jira SW Cloud API';
properties = [
{
displayName: 'Email',
name: 'email',
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'API Token',
name: 'apiToken',
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'Domain',
name: 'domain',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,83 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IExecuteSingleFunctions,
BINARY_ENCODING
} from 'n8n-core';
import {
IDataObject,
} from 'n8n-workflow';
export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('jiraSoftwareCloudApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
const data = Buffer.from(`${credentials!.email}:${credentials!.apiToken}`).toString(BINARY_ENCODING);
const headerWithAuthentication = Object.assign({},
{ Authorization: `Basic ${data}`, Accept: 'application/json', 'Content-Type': 'application/json' });
const options: OptionsWithUri = {
headers: headerWithAuthentication,
method,
qs: query,
uri: uri || `${credentials.domain}/rest/api/2${endpoint}`,
body,
json: true
};
try {
return await this.helpers.request!(options);
} catch (error) {
const errorMessage =
error.response.body.message || error.response.body.Message;
if (errorMessage !== undefined) {
throw errorMessage;
}
throw error.response.body;
}
}
/**
* Make an API request to paginated intercom endpoint
* and return all results
*/
export async function jiraSoftwareCloudApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
query.maxResults = 100;
let uri: string | undefined;
do {
responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query, uri);
uri = responseData.nextPage;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.isLast !== false &&
responseData.nextPage !== undefined &&
responseData.nextPage !== null
);
return returnData;
}
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
let result;
try {
result = JSON.parse(json!);
} catch (exception) {
result = '';
}
return result;
}

View file

@ -0,0 +1,856 @@
import { INodeProperties } from "n8n-workflow";
export const issueOpeations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'issue',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new issue',
},
{
name: 'Update',
value: 'update',
description: 'Update an issue',
},
{
name: 'Get',
value: 'get',
description: 'Get an issue',
},
{
name: 'Changelog',
value: 'changelog',
description: 'Get issue changelog',
},
{
name: 'Notify',
value: 'notify',
description: 'Creates an email notification for an issue and adds it to the mail queue.',
},
{
name: 'Transitions',
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.`,
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an issue',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const issueFields = [
/* -------------------------------------------------------------------------- */
/* issue:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Project',
name: 'project',
type: 'options',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'create'
]
},
},
typeOptions: {
loadOptionsMethod: 'getProjects',
},
description: 'Project',
},
{
displayName: 'Issue Type',
name: 'issueType',
type: 'options',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'create'
]
},
},
typeOptions: {
loadOptionsMethod: 'getIssueTypes',
},
description: 'Issue Types',
},
{
displayName: 'Summary',
name: 'summary',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'create',
],
},
},
default: '',
description: 'Summary',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'create',
],
},
},
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',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
required : false,
description: 'Assignee',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
required : false,
description: 'Description',
},
{
displayName: 'Update History',
name: 'updateHistory',
type: 'boolean',
default: false,
required : false,
description: `Whether the project in which the issue is created is added to the user's<br/>
Recently viewed project list, as shown under Projects in Jira.`,
},
],
},
/* -------------------------------------------------------------------------- */
/* issue:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'update',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'update',
],
},
},
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',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
required : false,
description: 'Assignee',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
required : false,
description: 'Description',
},
],
},
/* -------------------------------------------------------------------------- */
/* issue:delete */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'delete',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'Delete Subtasks',
name: 'deleteSubtasks',
type: 'boolean',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'delete',
],
},
},
default: false,
description: 'Delete Subtasks',
},
/* -------------------------------------------------------------------------- */
/* issue:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'get',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Fields',
name: 'fields',
type: 'string',
required: false,
default: '',
description: `A list of fields to return for the issue.<br/>
This parameter accepts a comma-separated list.<br/>
Use it to retrieve a subset of fields. Allowed values:<br/>
*all Returns all fields.<br/>
*navigable Returns navigable fields.<br/>
Any issue field, prefixed with a minus to exclude.<br/>`
},
{
displayName: 'Fields By Key',
name: 'fieldsByKey',
type: 'boolean',
required: false,
default: false,
description: `Indicates whether fields in fields are referenced by keys rather than IDs.<br/>
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',
type: 'string',
required: false,
default: '',
description: `A list of issue properties to return for the issue.<br/>
This parameter accepts a comma-separated list. Allowed values:<br/>
*all Returns all issue properties.<br/>
Any issue property key, prefixed with a minus to exclude.<br/>
Examples:<br/>
*all Returns all properties.<br/>
*all,-prop1 Returns all properties except prop1.<br/>
prop1,prop2 Returns prop1 and prop2 properties.<br/>
This parameter may be specified multiple times. For example, properties=prop1,prop2& properties=prop3.`
},
{
displayName: 'Update History',
name: 'updateHistory',
type: 'boolean',
required: false,
default: false,
description: `Whether the project in which the issue is created is added to the user's
Recently viewed project list, as shown under Projects in Jira. This also populates the
JQL issues search lastViewed field.`,
},
]
},
/* -------------------------------------------------------------------------- */
/* issue:changelog */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'changelog',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'changelog',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'changelog',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
/* -------------------------------------------------------------------------- */
/* issue:notify */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'JSON Parameters',
name: 'jsonParameters',
type: 'boolean',
default: false,
description: '',
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
},
},
options: [
{
displayName: 'Subject',
name: 'subject',
type: 'string',
required: false,
default: '',
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: 'Text Body',
name: 'textBody',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
required: false,
default: '',
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.',
},
],
},
{
displayName: 'Notification Recipients',
name: 'notificationRecipientsUi',
type: 'fixedCollection',
placeholder: 'Add Recipients',
typeOptions: {
multipleValues: false,
},
description: 'The recipients of the email notification for the issue.',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
jsonParameters: [
false,
],
},
},
options: [
{
name: 'notificationRecipientsValues',
displayName: 'Recipients',
values: [
{
displayName: 'Reporter',
name: 'reporter',
type: 'boolean',
description: `Indicates whether the notification should be sent to the issue's reporter.`,
default: false,
},
{
displayName: 'Assignee',
name: 'assignee',
type: 'boolean',
default: false,
description: `Indicates whether the notification should be sent to the issue's assignees.`,
},
{
displayName: 'Watchers',
name: 'watchers',
type: 'boolean',
default: false,
description: `Indicates whether the notification should be sent to the issue's assignees.`,
},
{
displayName: 'Voters',
name: 'voters',
type: 'boolean',
default: false,
description: `Indicates whether the notification should be sent to the issue's voters.`,
},
{
displayName: 'Users',
name: 'users',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
description: `List of users to receive the notification.`,
},
{
displayName: 'Groups',
name: 'groups',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getGroups',
},
default: [],
description: `List of groups to receive the notification.`,
},
]
}
]
},
{
displayName: 'Notification Recipients',
name: 'notificationRecipientsJson',
type: 'json',
typeOptions: {
alwaysOpenEditWindow: true,
},
required: false,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
jsonParameters: [
true,
]
},
},
default: '',
description: 'The recipients of the email notification for the issue.',
},
{
displayName: 'Notification Recipients Restrictions',
name: 'notificationRecipientsRestrictionsUi',
type: 'fixedCollection',
placeholder: 'Add Recipients Restriction',
typeOptions: {
multipleValues: false,
},
description: 'Restricts the notifications to users with the specified permissions.',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
jsonParameters: [
false,
],
},
},
options: [
{
name: 'notificationRecipientsRestrictionsValues',
displayName: 'Recipients Restrictions',
values: [
{
displayName: 'Users',
name: 'users',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
description: `List of users to receive the notification.`,
},
{
displayName: 'Groups',
name: 'groups',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getGroups',
},
default: [],
description: `List of groups to receive the notification.`,
},
]
}
]
},
{
displayName: 'Notification Recipients Restrictions',
name: 'notificationRecipientsRestrictionsJson',
type: 'json',
typeOptions: {
alwaysOpenEditWindow: true,
},
required: false,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'notify',
],
jsonParameters: [
true,
]
},
},
default: '',
description: 'Restricts the notifications to users with the specified permissions.',
},
/* -------------------------------------------------------------------------- */
/* issue:transitions */
/* -------------------------------------------------------------------------- */
{
displayName: 'Issue Key',
name: 'issueKey',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'transitions',
],
},
},
default: '',
description: 'Issue Key',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'transitions',
],
},
},
options: [
{
displayName: 'Expand',
name: 'expand',
type: 'string',
required: false,
default: '',
description: `Use expand to include additional information about transitions in the response.<br/>
This parameter accepts transitions.fields, which returns information about the fields in the<br/>
transition screen for each transition. Fields hidden from the screen are not returned. Use this<br/>
information to populate the fields and update fields in Transition issue.`
},
{
displayName: 'Transition ID',
name: 'transitionId',
type: 'string',
required: false,
default: '',
description: 'The ID of the transition.',
},
{
displayName: 'Skip Remote Only Condition',
name: 'skipRemoteOnlyCondition',
type: 'boolean',
required: false,
default: false,
description: `Indicates whether transitions with the condition Hide<br/>
From User Condition are included in the response.`,
},
],
},
] as INodeProperties[];

View file

@ -0,0 +1,37 @@
import { IDataObject } from "n8n-workflow";
export interface IFields {
summary?: string;
project?: IDataObject;
issuetype?: IDataObject;
labels?: string[];
priority?: IDataObject;
assignee?: IDataObject;
description?: string;
parent?: IDataObject;
}
export interface IIssue {
fields?: IFields;
}
export interface INotify {
subject?: string;
textBody?: string;
htmlBody?: string;
to?: INotificationRecipients;
restrict?: NotificationRecipientsRestrictions;
}
export interface INotificationRecipients {
reporter?: boolean;
assignee?: boolean;
watchers?: boolean;
voters?: boolean;
users?: IDataObject[];
groups?: IDataObject[];
}
export interface NotificationRecipientsRestrictions {
groups?: IDataObject[];
}

View file

@ -0,0 +1,498 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeTypeDescription,
INodeExecutionData,
INodeType,
ILoadOptionsFunctions,
INodePropertyOptions,
} from 'n8n-workflow';
import {
jiraSoftwareCloudApiRequest,
jiraSoftwareCloudApiRequestAllItems,
validateJSON,
} from './GenericFunctions';
import {
issueOpeations,
issueFields,
} from './IssueDescription';
import {
IIssue,
IFields,
INotify,
INotificationRecipients,
NotificationRecipientsRestrictions,
} from './IssueInterface';
export class JiraSoftwareCloud implements INodeType {
description: INodeTypeDescription = {
displayName: 'Jira Software Cloud',
name: 'Jira Software Cloud',
icon: 'file:jira.png',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Jira Software Cloud API',
defaults: {
name: 'Jira Software Cloud',
color: '#c02428',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'jiraSoftwareCloudApi',
required: true,
}
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Issue',
value: 'issue',
description: 'Creates an issue or, where the option to create subtasks is enabled in Jira, a subtask',
},
],
default: 'issue',
description: 'Resource to consume.',
},
...issueOpeations,
...issueFields,
],
};
methods = {
loadOptions: {
// Get all the projects to display them to user so that he can
// select them easily
async getProjects(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
let projects;
try {
projects = await jiraSoftwareCloudApiRequest.call(this, '/project/search', 'GET');
} catch (err) {
throw new Error(`Jira Error: ${err}`);
}
for (const project of projects.values) {
const projectName = project.name;
const projectId = project.id;
returnData.push({
name: projectName,
value: projectId,
});
}
return returnData;
},
// Get all the issue types to display them to user so that he can
// select them easily
async getIssueTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
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,
});
}
return returnData;
},
// Get all the labels to display them to user so that he can
// select them easily
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;
returnData.push({
name: labelName,
value: labelId,
});
}
return returnData;
},
// Get all the priorities to display them to user so that he can
// select them easily
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;
returnData.push({
name: priorityName,
value: priorityId,
});
}
return returnData;
},
// Get all the users to display them to user so that he can
// select them easily
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;
returnData.push({
name: userName,
value: userId,
});
}
return returnData;
},
// Get all the groups to display them to user so that he can
// select them easily
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;
returnData.push({
name: groupName,
value: groupId,
});
}
return returnData;
}
}
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
let responseData;
const qs: IDataObject = {};
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < length; i++) {
if (resource === 'issue') {
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-post
if (operation === 'create') {
const summary = this.getNodeParameter('summary', i) as string;
const projectId = this.getNodeParameter('project', i) as string;
const issueTypeId = this.getNodeParameter('issueType', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const body: IIssue = {};
const fields: IFields = {
summary,
project: {
id: projectId,
},
issuetype: {
id: issueTypeId,
},
};
if (additionalFields.labels) {
fields.labels = additionalFields.labels as string[];
}
if (additionalFields.priority) {
fields.priority = {
id: additionalFields.priority as string,
};
}
if (additionalFields.assignee) {
fields.assignee = {
id: additionalFields.assignee as string,
};
}
if (additionalFields.description) {
fields.description = additionalFields.description as string;
}
if (additionalFields.updateHistory) {
qs.updateHistory = additionalFields.updateHistory as boolean;
}
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/issuetype', 'GET', body, qs);
const subtaskIssues = [];
for (const issueType of issueTypes) {
if (issueType.subtask) {
subtaskIssues.push(issueType.id);
}
}
if (!additionalFields.parentIssueKey
&& subtaskIssues.includes(issueTypeId)) {
throw new Error('You must define a Parent Issue Key when Issue type is sub-task');
} else if (additionalFields.parentIssueKey
&& subtaskIssues.includes(issueTypeId)) {
fields.parent = {
key: (additionalFields.parentIssueKey as string).toUpperCase(),
};
}
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') {
const issueKey = this.getNodeParameter('issueKey', i) as string;
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
const body: IIssue = {};
const fields: IFields = {};
if (updateFields.summary) {
fields.summary = updateFields.summary as string;
}
if (updateFields.issueType) {
fields.issuetype = {
id: updateFields.issueType as string,
};
}
if (updateFields.labels) {
fields.labels = updateFields.labels as string[];
}
if (updateFields.priority) {
fields.priority = {
id: updateFields.priority as string,
};
}
if (updateFields.assignee) {
fields.assignee = {
id: updateFields.assignee as string,
};
}
if (updateFields.description) {
fields.description = updateFields.description as string;
}
const issueTypes = await jiraSoftwareCloudApiRequest.call(this, '/issuetype', 'GET', body);
const subtaskIssues = [];
for (const issueType of issueTypes) {
if (issueType.subtask) {
subtaskIssues.push(issueType.id);
}
}
if (!updateFields.parentIssueKey
&& subtaskIssues.includes(updateFields.issueType)) {
throw new Error('You must define a Parent Issue Key when Issue type is sub-task');
} else if (updateFields.parentIssueKey
&& subtaskIssues.includes(updateFields.issueType)) {
fields.parent = {
key: (updateFields.parentIssueKey as string).toUpperCase(),
};
}
body.fields = fields;
try {
responseData = await jiraSoftwareCloudApiRequest.call(this, `/issue/${issueKey}`, 'PUT', 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-get
if (operation === 'get') {
const issueKey = this.getNodeParameter('issueKey', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.fields) {
qs.fields = additionalFields.fields as string;
}
if (additionalFields.fieldsByKey) {
qs.fieldsByKey = additionalFields.fieldsByKey as boolean;
}
if (additionalFields.expand) {
qs.expand = additionalFields.expand as string;
}
if (additionalFields.properties) {
qs.properties = additionalFields.properties as string;
}
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-issue-issueIdOrKey-changelog-get
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)}`);
}
}
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-notify-post
if (operation === 'notify') {
const issueKey = this.getNodeParameter('issueKey', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const jsonActive = this.getNodeParameter('jsonParameters', 0) as boolean;
const body: INotify = {};
if (additionalFields.textBody) {
body.textBody = additionalFields.textBody as string;
}
if (additionalFields.htmlBody) {
body.htmlBody = additionalFields.htmlBody as string;
}
if (!jsonActive) {
const notificationRecipientsValues = (this.getNodeParameter('notificationRecipientsUi', i) as IDataObject).notificationRecipientsValues as IDataObject[];
const notificationRecipients: INotificationRecipients = {};
if (notificationRecipientsValues) {
// @ts-ignore
if (notificationRecipientsValues.reporter) {
// @ts-ignore
notificationRecipients.reporter = notificationRecipientsValues.reporter as boolean;
}
// @ts-ignore
if (notificationRecipientsValues.assignee) {
// @ts-ignore
notificationRecipients.assignee = notificationRecipientsValues.assignee as boolean;
}
// @ts-ignore
if (notificationRecipientsValues.assignee) {
// @ts-ignore
notificationRecipients.watchers = notificationRecipientsValues.watchers as boolean;
}
// @ts-ignore
if (notificationRecipientsValues.voters) {
// @ts-ignore
notificationRecipients.watchers = notificationRecipientsValues.voters as boolean;
}
// @ts-ignore
if (notificationRecipientsValues.users.length > 0) {
// @ts-ignore
notificationRecipients.users = notificationRecipientsValues.users.map(user => {
return {
accountId: user
};
});
}
// @ts-ignore
if (notificationRecipientsValues.groups.length > 0) {
// @ts-ignore
notificationRecipients.groups = notificationRecipientsValues.groups.map(group => {
return {
name: group
};
});
}
}
body.to = notificationRecipients;
const notificationRecipientsRestrictionsValues = (this.getNodeParameter('notificationRecipientsRestrictionsUi', i) as IDataObject).notificationRecipientsRestrictionsValues as IDataObject[];
const notificationRecipientsRestrictions: NotificationRecipientsRestrictions = {};
if (notificationRecipientsRestrictionsValues) {
// @ts-ignore
if (notificationRecipientsRestrictionsValues.groups. length > 0) {
// @ts-ignore
notificationRecipientsRestrictions.groups = notificationRecipientsRestrictionsValues.groups.map(group => {
return {
name: group
};
});
}
}
body.restrict = notificationRecipientsRestrictions;
} else {
const notificationRecipientsJson = validateJSON(this.getNodeParameter('notificationRecipientsJson', i) as string);
if (notificationRecipientsJson) {
body.to = notificationRecipientsJson;
}
const notificationRecipientsRestrictionsJson = validateJSON(this.getNodeParameter('notificationRecipientsRestrictionsJson', i) as string);
if (notificationRecipientsRestrictionsJson) {
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') {
const issueKey = this.getNodeParameter('issueKey', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.transitionId) {
qs.transitionId = additionalFields.transitionId as string;
}
if (additionalFields.expand) {
qs.expand = additionalFields.expand as string;
}
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)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -42,7 +42,8 @@
"dist/credentials/HttpDigestAuth.credentials.js",
"dist/credentials/HttpHeaderAuth.credentials.js",
"dist/credentials/IntercomApi.credentials.js",
"dist/credentials/Imap.credentials.js",
"dist/credentials/Imap.credentials.js",
"dist/credentials/JiraSoftwareCloudApi.credentials.js",
"dist/credentials/LinkFishApi.credentials.js",
"dist/credentials/MailchimpApi.credentials.js",
"dist/credentials/MailgunApi.credentials.js",
@ -102,6 +103,7 @@
"dist/nodes/If.node.js",
"dist/nodes/Interval.node.js",
"dist/nodes/Intercom/Intercom.node.js",
"dist/nodes/Jira/JiraSoftwareCloud.node.js",
"dist/nodes/LinkFish/LinkFish.node.js",
"dist/nodes/Mailchimp/Mailchimp.node.js",
"dist/nodes/Mailgun/Mailgun.node.js",