diff --git a/packages/nodes-base/credentials/JiraSoftwareCloudApi.credentials.ts b/packages/nodes-base/credentials/JiraSoftwareCloudApi.credentials.ts index a40cac2bdc..a11d526f7d 100644 --- a/packages/nodes-base/credentials/JiraSoftwareCloudApi.credentials.ts +++ b/packages/nodes-base/credentials/JiraSoftwareCloudApi.credentials.ts @@ -5,77 +5,23 @@ import { export class JiraSoftwareCloudApi implements ICredentialType { name = 'jiraSoftwareCloudApi'; - displayName = 'Jira SW API'; + displayName = 'Jira SW Cloud API'; properties = [ - { - displayName: 'Jira Version', - name: 'jiraVersion', - type: 'options' as NodePropertyTypes, - options: [ - { - name: 'Cloud', - value: 'cloud', - }, - { - name: 'Server (Self Hosted)', - value: 'server', - }, - ], - default: 'cloud', - }, { displayName: 'Email', name: 'email', - displayOptions: { - show: { - jiraVersion: [ - 'cloud', - ], - }, - }, type: 'string' as NodePropertyTypes, default: '', }, { displayName: 'API Token', name: 'apiToken', - displayOptions: { - show: { - jiraVersion: [ - 'cloud', - ], - }, - }, - type: 'string' as NodePropertyTypes, - default: '', - }, - { - displayName: 'Password', - name: 'password', - displayOptions: { - show: { - jiraVersion: [ - 'server', - ], - }, - }, - typeOptions: { - password: true, - }, type: 'string' as NodePropertyTypes, default: '', }, { displayName: 'Domain', name: 'domain', - displayOptions: { - show: { - jiraVersion: [ - 'cloud', - 'server', - ], - }, - }, type: 'string' as NodePropertyTypes, default: '', }, diff --git a/packages/nodes-base/credentials/JiraSoftwareServerApi.credentials.ts b/packages/nodes-base/credentials/JiraSoftwareServerApi.credentials.ts new file mode 100644 index 0000000000..47d94eb6f0 --- /dev/null +++ b/packages/nodes-base/credentials/JiraSoftwareServerApi.credentials.ts @@ -0,0 +1,32 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class JiraSoftwareServerApi implements ICredentialType { + name = 'jiraSoftwareServerApi'; + displayName = 'Jira SW Server API'; + properties = [ + { + displayName: 'Email', + name: 'email', + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Password', + name: 'password', + typeOptions: { + password: true, + }, + type: 'string' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Domain', + name: 'domain', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Jira/GenericFunctions.ts b/packages/nodes-base/nodes/Jira/GenericFunctions.ts index fb8473d27c..421f508193 100644 --- a/packages/nodes-base/nodes/Jira/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Jira/GenericFunctions.ts @@ -13,18 +13,27 @@ import { } from 'n8n-workflow'; export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('jiraSoftwareCloudApi'); - if (credentials === undefined) { + let data; let domain; + const jiraCloudCredentials = this.getCredentials('jiraSoftwareCloudApi'); + const jiraServerCredentials = this.getCredentials('jiraSoftwareServerApi'); + if (jiraCloudCredentials === undefined + && jiraServerCredentials === undefined) { throw new Error('No credentials got returned!'); } - const data = Buffer.from(`${credentials!.email}:${(credentials.jiraVersion === 'server') ? credentials.password : credentials.apiToken }`).toString(BINARY_ENCODING); + if (jiraCloudCredentials !== undefined) { + domain = jiraCloudCredentials!.domain; + data = Buffer.from(`${jiraCloudCredentials!.email}:${jiraCloudCredentials!.apiToken}`).toString(BINARY_ENCODING); + } else { + domain = jiraServerCredentials!.domain; + data = Buffer.from(`${jiraServerCredentials!.email}:${jiraServerCredentials!.password}`).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}`, + uri: uri || `${domain}/rest/api/2${endpoint}`, body, json: true }; @@ -32,13 +41,11 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut try { return await this.helpers.request!(options); } catch (error) { - const errorMessage = - error.response.body.message || error.response.body.Message; - - if (errorMessage !== undefined) { - throw errorMessage; + let errorMessage = error; + if (error.error && error.error.errorMessages) { + errorMessage = error.error.errorMessages; } - throw error.response.body; + throw new Error(errorMessage); } } @@ -48,18 +55,18 @@ export async function jiraSoftwareCloudApiRequestAllItems(this: IHookFunctions | let responseData; + query.startAt = 0; + body.startAt = 0; query.maxResults = 100; - - let uri: string | undefined; + body.maxResults = 100; do { - responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query, uri); - uri = responseData.nextPage; + responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query); returnData.push.apply(returnData, responseData[propertyName]); + query.startAt = responseData.startAt + responseData.maxResults; + body.startAt = responseData.startAt + responseData.maxResults; } while ( - responseData.isLast !== false && - responseData.nextPage !== undefined && - responseData.nextPage !== null + (responseData.startAt + responseData.maxResults < responseData.total) ); return returnData; diff --git a/packages/nodes-base/nodes/Jira/IssueDescription.ts b/packages/nodes-base/nodes/Jira/IssueDescription.ts index f185557f5f..3712f9f047 100644 --- a/packages/nodes-base/nodes/Jira/IssueDescription.ts +++ b/packages/nodes-base/nodes/Jira/IssueDescription.ts @@ -28,6 +28,11 @@ export const issueOperations = [ value: 'get', description: 'Get an issue', }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all issues', + }, { name: 'Changelog', value: 'changelog', @@ -452,6 +457,148 @@ export const issueFields = [ }, /* -------------------------------------------------------------------------- */ +/* issue:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + 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: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 100, + }, + default: 50, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'issue', + ], + }, + }, + default: {}, + options: [ + { + displayName: ' JQL', + name: 'jql', + type: 'string', + default: '', + typeOptions: { + alwaysOpenEditWindow: true, + }, + description: 'A JQL expression.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '*navigable', + description: `A list of fields to return for each issue, use it to retrieve a subset of fields. This parameter accepts a comma-separated list. Expand options include:
+ *all Returns all fields.
+ *navigable Returns navigable fields.
+ Any issue field, prefixed with a minus to exclude.
`, + }, + { + displayName: 'Expand', + name: 'expand', + type: 'options', + default: '', + options: [ + { + name: 'Rendered Fields', + valie: 'renderedFields', + description: ' Returns field values rendered in HTML format.', + }, + { + name: 'Names', + valie: 'names', + description: 'Returns the display name of each field', + }, + { + name: 'Schema', + valie: 'schema', + description: 'Returns the schema describing a field type.', + }, + { + name: 'Transitions', + valie: 'transitions', + description: ' Returns all possible transitions for the issue.', + }, + { + name: 'Operations', + valie: 'operations', + description: 'Returns all possible operations for the issue.', + }, + { + name: 'Editmeta', + valie: 'editmeta', + description: 'Returns information about how each field can be edited', + }, + { + name: 'Changelog', + valie: 'changelog', + description: 'Returns a list of recent updates to an issue, sorted by date, starting from the most recent.', + }, + { + name: 'Versioned Representations', + valie: 'versionedRepresentations', + description: `JSON array containing each version of a field's value`, + }, + ], + description: `Use expand to include additional information about issues in the response`, + }, + { + 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.
+ This parameter is useful where fields have been added by a connect app and a field's key
+ may differ from its ID.`, + }, + ], + }, +/* -------------------------------------------------------------------------- */ /* issue:changelog */ /* -------------------------------------------------------------------------- */ { diff --git a/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts b/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts index 689c902176..b4e3cf0c2a 100644 --- a/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts +++ b/packages/nodes-base/nodes/Jira/JiraSoftwareCloud.node.ts @@ -45,9 +45,43 @@ export class JiraSoftwareCloud implements INodeType { { name: 'jiraSoftwareCloudApi', required: true, + displayOptions: { + show: { + jiraVersion: [ + 'cloud', + ], + }, + }, + }, + { + name: 'jiraSoftwareServerApi', + required: true, + displayOptions: { + show: { + jiraVersion: [ + 'server', + ], + }, + }, }, ], properties: [ + { + displayName: 'Jira Version', + name: 'jiraVersion', + type: 'options', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Server (Self Hosted)', + value: 'server', + }, + ], + default: 'cloud', + }, { displayName: 'Resource', name: 'resource', @@ -73,10 +107,10 @@ export class JiraSoftwareCloud implements INodeType { // select them easily async getProjects(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const credentials = this.getCredentials('jiraSoftwareCloudApi'); + const jiraCloudCredentials = this.getCredentials('jiraSoftwareCloudApi'); let projects; let endpoint = '/project/search'; - if (credentials!.jiraVersion === 'server') { + if (jiraCloudCredentials === undefined) { endpoint = '/project'; } try { @@ -360,6 +394,29 @@ export class JiraSoftwareCloud implements INodeType { 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') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + const body: IDataObject = {}; + if (options.fields) { + body.fields = (options.fields as string).split(',') as string[]; + } + if (options.jql) { + body.jql = options.jql as string; + } + if (options.expand) { + body.expand = options.expand as string; + } + if (returnAll) { + responseData = await jiraSoftwareCloudApiRequestAllItems.call(this, 'issues', `/search`, 'POST', body); + } else { + const limit = this.getNodeParameter('limit', i) as number; + body.maxResults = limit; + responseData = await jiraSoftwareCloudApiRequest.call(this, `/search`, 'POST', body); + responseData = responseData.issues; + } + } //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; diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index b59b37befd..2838e86573 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -58,6 +58,7 @@ "dist/credentials/Imap.credentials.js", "dist/credentials/IntercomApi.credentials.js", "dist/credentials/JiraSoftwareCloudApi.credentials.js", + "dist/credentials/JiraSoftwareServerApi.credentials.js", "dist/credentials/JotFormApi.credentials.js", "dist/credentials/LinkFishApi.credentials.js", "dist/credentials/MailchimpApi.credentials.js",