Skip to content

Commit

Permalink
feat(Jira Node): Add Simplify Output option to Issue > Get (#2408)
Browse files Browse the repository at this point in the history
* ✨ Add option to use Jira field display names

* 🚸 Make mapped fields more deterministic

* ♻️ Refactor Jira user loadOptions

* Moved and renamed the option as well as only returning the fields to

* Tweaked Friendly Fields to make it "Simplify Output" following similar patterns to other nodes

* ⚡ Improvements

Co-authored-by: Jonathan Bennetts <jonathan.bennetts@gmail.com>
Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
  • Loading branch information
3 people authored Mar 27, 2022
1 parent 5ba4c27 commit 016aeaa
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 39 deletions.
46 changes: 45 additions & 1 deletion packages/nodes-base/nodes/Jira/GenericFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import {
ICredentialDataDecryptedObject,
IDataObject,
JsonObject,
NodeApiError,
NodeOperationError,
} from 'n8n-workflow';
Expand Down Expand Up @@ -69,7 +70,7 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut
try {
return await this.helpers.request!(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error);
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}

Expand Down Expand Up @@ -119,6 +120,49 @@ export function getId(url: string) {
return url.split('/').pop();
}

export function simplifyIssueOutput(responseData: {
names: { [key: string]: string },
fields: IDataObject,
id: string,
key: string,
self: string
}) {
const mappedFields: IDataObject = {
id: responseData.id,
key: responseData.key,
self: responseData.self,
};
// Sort custom fields last so we map them last
const customField = /^customfield_\d+$/;
const sortedFields: string[] = Object.keys(responseData.fields).sort((a, b) => {
if (customField.test(a) && customField.test(b)) {
return a > b ? 1 : -1;
}
if (customField.test(a)) {
return 1;
}
if (customField.test(b)) {
return -1;
}
return a > b ? 1 : -1;
});
for (const field of sortedFields) {
if (responseData.names[field] in mappedFields) {
let newField: string = responseData.names[field];
let counter = 0;
while (newField in mappedFields) {
counter++;
newField = `${responseData.names[field]}_${counter}`;
}
mappedFields[newField] = responseData.fields[field];
} else {
mappedFields[responseData.names[field] || field] = responseData.fields[field];
}
}

return mappedFields;
}

export const allEvents = [
'board_created',
'board_updated',
Expand Down
18 changes: 18 additions & 0 deletions packages/nodes-base/nodes/Jira/IssueDescription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,24 @@ export const issueFields: INodeProperties[] = [
default: '',
description: 'Issue Key',
},
{
displayName: 'Simplify Output',
name: 'simplifyOutput',
type: 'boolean',
required: false,
displayOptions: {
show: {
resource: [
'issue',
],
operation: [
'get',
],
},
},
default: false,
description: `Return a simplified output of the issues fields.`,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
Expand Down
71 changes: 33 additions & 38 deletions packages/nodes-base/nodes/Jira/Jira.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import {
INodePropertyOptions,
INodeType,
INodeTypeDescription,
JsonObject,
NodeOperationError,
} from 'n8n-workflow';

import {
jiraSoftwareCloudApiRequest,
jiraSoftwareCloudApiRequestAllItems,
simplifyIssueOutput,
validateJSON,
} from './GenericFunctions';

Expand Down Expand Up @@ -178,7 +180,7 @@ export class Jira implements INodeType {
} catch (err) {
return {
status: 'Error',
message: `Connection details not valid: ${err.message}`,
message: `Connection details not valid: ${(err as JsonObject).message}`,
};
}
return {
Expand Down Expand Up @@ -303,45 +305,29 @@ export class Jira implements INodeType {
// 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[] = [];
const jiraVersion = this.getCurrentNodeParameter('jiraVersion') as string;
if (jiraVersion === 'server') {
// the interface call must bring username
const users = await jiraSoftwareCloudApiRequest.call(this, '/api/2/user/search', 'GET', {},
{
username: '\'',
},
);
for (const user of users) {
const userName = user.displayName;
const userId = user.name;
const query: IDataObject = {};
let endpoint = '/api/2/users/search';

returnData.push({
name: userName,
value: userId,
});
}
} else {
const users = await jiraSoftwareCloudApiRequest.call(this, '/api/2/users/search', 'GET');
if (jiraVersion === 'server') {
endpoint = '/api/2/user/search';
query.username = '\'';
}

for (const user of users) {
const userName = user.displayName;
const userId = user.accountId;
const users = await jiraSoftwareCloudApiRequest.call(this, endpoint, 'GET', {}, query);

returnData.push({
name: userName,
value: userId,
return users.reduce((activeUsers: INodePropertyOptions[], user: IDataObject) => {
if (user.active) {
activeUsers.push({
name: user.displayName as string,
value: (user.accountId || user.name) as string,
});
}
}

returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
return activeUsers;
}, []).sort((a: INodePropertyOptions, b: INodePropertyOptions) => {
return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1;
});

return returnData;
},

// Get all the groups to display them to user so that he can
Expand Down Expand Up @@ -418,10 +404,10 @@ export class Jira implements INodeType {
for (const key of Object.keys(fields)) {
const field = fields[key];
if (field.schema && Object.keys(field.schema).includes('customId')) {
returnData.push({
name: field.name,
value: field.key || field.fieldId,
});
returnData.push({
name: field.name,
value: field.key || field.fieldId,
});
}
}
return returnData;
Expand Down Expand Up @@ -642,6 +628,7 @@ export class Jira implements INodeType {
if (operation === 'get') {
for (let i = 0; i < length; i++) {
const issueKey = this.getNodeParameter('issueKey', i) as string;
const simplifyOutput = this.getNodeParameter('simplifyOutput', i) as boolean;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
if (additionalFields.fields) {
qs.fields = additionalFields.fields as string;
Expand All @@ -652,14 +639,22 @@ export class Jira implements INodeType {
if (additionalFields.expand) {
qs.expand = additionalFields.expand as string;
}
if (simplifyOutput) {
qs.expand = `${qs.expand || ''},names`;
}
if (additionalFields.properties) {
qs.properties = additionalFields.properties as string;
}
if (additionalFields.updateHistory) {
qs.updateHistory = additionalFields.updateHistory as string;
}
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, qs);
returnData.push(responseData);

if (simplifyOutput) {
returnData.push(simplifyIssueOutput(responseData));
} else {
returnData.push(responseData);
}
}
}
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
Expand Down Expand Up @@ -689,7 +684,7 @@ export class Jira implements INodeType {
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/search`, 'POST', body);
responseData = responseData.issues;
}
returnData.push.apply(returnData, responseData);
returnData.push(...responseData);
}
}
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-changelog-get
Expand Down

0 comments on commit 016aeaa

Please sign in to comment.