diff --git a/packages/cli/BREAKING-CHANGES.md b/packages/cli/BREAKING-CHANGES.md index a7ac4db33b..fb393e0f3c 100644 --- a/packages/cli/BREAKING-CHANGES.md +++ b/packages/cli/BREAKING-CHANGES.md @@ -2,6 +2,16 @@ This list shows all the versions which include breaking changes and how to upgrade. +## 0.130.0 + +### What changed? + +For the Taiga regular and trigger nodes, the server and cloud credentials types are now unified into a single credentials type and the `version` param has been removed. Also, the `issue:create` operation now automatically loads the tags as `multiOptions`. + +### When is action necessary? + +If you are using the Taiga nodes, reconnect the credentials. If you are using tags in the `issue:create` operation, reselect them. + ## 0.127.0 ### What changed? diff --git a/packages/nodes-base/credentials/TaigaServerApi.credentials.ts b/packages/nodes-base/credentials/TaigaApi.credentials.ts similarity index 51% rename from packages/nodes-base/credentials/TaigaServerApi.credentials.ts rename to packages/nodes-base/credentials/TaigaApi.credentials.ts index 99c7125fec..db5e7ec6ad 100644 --- a/packages/nodes-base/credentials/TaigaServerApi.credentials.ts +++ b/packages/nodes-base/credentials/TaigaApi.credentials.ts @@ -3,9 +3,9 @@ import { INodeProperties, } from 'n8n-workflow'; -export class TaigaServerApi implements ICredentialType { - name = 'taigaServerApi'; - displayName = 'Taiga Server API'; +export class TaigaApi implements ICredentialType { + name = 'taigaApi'; + displayName = 'Taiga API'; documentationUrl = 'taiga'; properties: INodeProperties[] = [ { @@ -20,12 +20,35 @@ export class TaigaServerApi implements ICredentialType { type: 'string', default: '', }, + { + displayName: 'Environment', + name: 'environment', + type: 'options', + default: 'cloud', + options: [ + { + name: 'Cloud', + value: 'cloud', + }, + { + name: 'Self-Hosted', + value: 'selfHosted', + }, + ], + }, { displayName: 'URL', name: 'url', type: 'string', default: '', placeholder: 'https://taiga.yourdomain.com', + displayOptions: { + show: { + environment: [ + 'selfHosted', + ], + }, + }, }, ]; } diff --git a/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts b/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts deleted file mode 100644 index 2a32b29873..0000000000 --- a/packages/nodes-base/credentials/TaigaCloudApi.credentials.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - ICredentialType, - INodeProperties, -} from 'n8n-workflow'; - -export class TaigaCloudApi implements ICredentialType { - name = 'taigaCloudApi'; - displayName = 'Taiga Cloud API'; - documentationUrl = 'taiga'; - properties: INodeProperties[] = [ - { - displayName: 'Username', - name: 'username', - type: 'string', - default: '', - }, - { - displayName: 'Password', - name: 'password', - type: 'string', - default: '', - }, - ]; -} diff --git a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts index 2941bba021..d6c8a4a10c 100644 --- a/packages/nodes-base/nodes/Taiga/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Taiga/GenericFunctions.ts @@ -60,16 +60,7 @@ export async function taigaApiRequest( uri?: string | undefined, option = {}, ): Promise { // tslint:disable-line:no-any - - const version = this.getNodeParameter('version', 0, 'cloud') as string; - - let credentials; - - if (version === 'server') { - credentials = this.getCredentials('taigaServerApi') as ICredentialDataDecryptedObject; - } else { - credentials = this.getCredentials('taigaCloudApi') as ICredentialDataDecryptedObject; - } + const credentials = this.getCredentials('taigaApi') as ICredentialDataDecryptedObject; const authToken = await getAuthorization.call(this, credentials); @@ -124,3 +115,44 @@ export function getAutomaticSecret(credentials: ICredentialDataDecryptedObject) const data = `${credentials.username},${credentials.password}`; return createHash('md5').update(data).digest('hex'); } + +export async function handleListing( + this: IExecuteFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs: IDataObject = {}, + i: number, +) { + let responseData; + qs.project = this.getNodeParameter('projectId', i) as number; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + if (returnAll) { + return await taigaApiRequestAllItems.call(this, method, endpoint, body, qs); + } else { + qs.limit = this.getNodeParameter('limit', i) as number; + responseData = await taigaApiRequestAllItems.call(this, method, endpoint, body, qs); + return responseData.splice(0, qs.limit); + } +} + +export const toOptions = (items: LoadedResource[]) => + items.map(({ name, id }) => ({ name, value: id })); + +export function throwOnEmptyUpdate( + this: IExecuteFunctions, + resource: Resource, +) { + throw new NodeOperationError( + this.getNode(), + `Please enter at least one field to update for the ${resource}.`, + ); +} + +export async function getVersionForUpdate( + this: IExecuteFunctions, + endpoint: string, +) { + return await taigaApiRequest.call(this, 'GET', endpoint).then(response => response.version); +} diff --git a/packages/nodes-base/nodes/Taiga/IssueOperations.ts b/packages/nodes-base/nodes/Taiga/IssueOperations.ts deleted file mode 100644 index b20d1e439e..0000000000 --- a/packages/nodes-base/nodes/Taiga/IssueOperations.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - INodeProperties, -} from 'n8n-workflow'; - -export const issueOperations = [ - { - displayName: 'Operation', - name: 'operation', - type: 'options', - options: [ - { - name: 'Create', - value: 'create', - description: 'Create an issue', - }, - { - name: 'Delete', - value: 'delete', - description: 'Delete an issue', - }, - { - name: 'Get', - value: 'get', - description: 'Get an issue', - }, - { - name: 'Get All', - value: 'getAll', - description: 'Get all issues', - }, - { - name: 'Update', - value: 'update', - description: 'Update an issue', - }, - ], - default: 'create', - description: 'The operation to perform.', - }, -] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/Taiga.node.json b/packages/nodes-base/nodes/Taiga/Taiga.node.json index d80ba19d79..5ed38b7bef 100644 --- a/packages/nodes-base/nodes/Taiga/Taiga.node.json +++ b/packages/nodes-base/nodes/Taiga/Taiga.node.json @@ -3,6 +3,7 @@ "nodeVersion": "1.0", "codexVersion": "1.0", "categories": [ + "Development", "Productivity" ], "resources": { @@ -17,4 +18,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/nodes-base/nodes/Taiga/Taiga.node.ts b/packages/nodes-base/nodes/Taiga/Taiga.node.ts index dd888afc68..b3eed338d3 100644 --- a/packages/nodes-base/nodes/Taiga/Taiga.node.ts +++ b/packages/nodes-base/nodes/Taiga/Taiga.node.ts @@ -12,23 +12,29 @@ import { } from 'n8n-workflow'; import { + getVersionForUpdate, + handleListing, taigaApiRequest, - taigaApiRequestAllItems, + throwOnEmptyUpdate, + toOptions, } from './GenericFunctions'; import { + epicFields, + epicOperations, + issueFields, issueOperations, -} from './IssueOperations'; - -import { - issueOperationFields, -} from './issueOperationFields'; + taskFields, + taskOperations, + userStoryFields, + userStoryOperations, +} from './descriptions'; export class Taiga implements INodeType { description: INodeTypeDescription = { displayName: 'Taiga', name: 'taiga', - icon: 'file:taiga.png', + icon: 'file:taiga.svg', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', @@ -41,196 +47,139 @@ export class Taiga implements INodeType { outputs: ['main'], credentials: [ { - name: 'taigaCloudApi', - displayOptions: { - show: { - version: [ - 'cloud', - ], - }, - }, - required: true, - }, - { - name: 'taigaServerApi', - displayOptions: { - show: { - version: [ - 'server', - ], - }, - }, + name: 'taigaApi', required: true, }, ], properties: [ - { - displayName: 'Taiga Version', - name: 'version', - type: 'options', - options: [ - { - name: 'Cloud', - value: 'cloud', - }, - { - name: 'Server (Self Hosted)', - value: 'server', - }, - ], - default: 'cloud', - }, { displayName: 'Resource', name: 'resource', type: 'options', options: [ + { + name: 'Epic', + value: 'epic', + }, { name: 'Issue', value: 'issue', }, + { + name: 'Task', + value: 'task', + }, + { + name: 'User Story', + value: 'userStory', + }, ], default: 'issue', - description: 'Resource to consume.', }, + ...epicOperations, + ...epicFields, ...issueOperations, - ...issueOperationFields, + ...issueFields, + ...taskOperations, + ...taskFields, + ...userStoryOperations, + ...userStoryFields, ], }; methods = { loadOptions: { - // Get all the available tags to display them to user so that he can - // select them easily + async getEpics(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const epics = await taigaApiRequest.call(this, 'GET', '/epics', {}, { project }) as LoadedEpic[]; + + return epics.map(({ subject, id }) => ({ name: subject, value: id })); + }, + + async getMilestones(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const milestones = await taigaApiRequest.call(this, 'GET', '/milestones', {}, { project }) as LoadedResource[]; + + return toOptions(milestones); + }, + + async getPriorities(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const priorities = await taigaApiRequest.call(this, 'GET', '/priorities', {}, { project }) as LoadedResource[]; + + return toOptions(priorities); + }, + + async getProjects(this: ILoadOptionsFunctions): Promise { + const { id } = await taigaApiRequest.call(this, 'GET', '/users/me') as { id: string }; + const projects = await taigaApiRequest.call(this, 'GET', '/projects', {}, { member: id }) as LoadedResource[]; + + return toOptions(projects); + }, + + async getRoles(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const roles = await taigaApiRequest.call(this, 'GET', '/roles', {}, { project }) as LoadedResource[]; + + return toOptions(roles); + }, + + async getSeverities(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const severities = await taigaApiRequest.call(this, 'GET', '/severities', {}, { project }) as LoadedResource[]; + + return toOptions(severities); + }, + + async getTags(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const tags = await taigaApiRequest.call(this, 'GET', `/projects/${project}/tags_colors`) as LoadedTags; + + return Object.keys(tags).map(tag => ({ name: tag, value: tag })); + }, + async getTypes(this: ILoadOptionsFunctions): Promise { - const projectId = this.getCurrentNodeParameter('projectId') as string; + const project = this.getCurrentNodeParameter('projectId') as string; + const types = await taigaApiRequest.call(this, 'GET', '/issue-types', {}, { project }) as LoadedResource[]; - const returnData: INodePropertyOptions[] = []; - - const types = await taigaApiRequest.call(this, 'GET', `/issue-types?project=${projectId}`); - for (const type of types) { - const typeName = type.name; - const typeId = type.id; - returnData.push({ - name: typeName, - value: typeId, - }); - } - return returnData; + return toOptions(types); }, - // Get all the available statuses to display them to user so that he can - // select them easily - async getStatuses(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + async getUsers(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const users = await taigaApiRequest.call(this, 'GET', '/users', {}, { project }) as LoadedUser[]; - const projectId = this.getCurrentNodeParameter('projectId') as string; - - const statuses = await taigaApiRequest.call(this, 'GET', '/issue-statuses', {}, { project: projectId }); - for (const status of statuses) { - const statusName = status.name; - const statusId = status.id; - returnData.push({ - name: statusName, - value: statusId, - }); - } - return returnData; + return users.map(({ full_name_display, id }) => ({ name: full_name_display, value: id })); }, - // Get all the available users to display them to user so that he can - // select them easily - async getProjectUsers(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + async getUserStories(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const userStories = await taigaApiRequest.call(this, 'GET', '/userstories', {}, { project }) as LoadedUserStory[]; - const projectId = this.getCurrentNodeParameter('projectId') as string; - - const users = await taigaApiRequest.call(this, 'GET', '/users', {}, { project: projectId }); - for (const user of users) { - const userName = user.username; - const userId = user.id; - returnData.push({ - name: userName, - value: userId, - }); - } - return returnData; + return userStories.map(({ subject, id }) => ({ name: subject, value: id })); }, - // Get all the available priorities to display them to user so that he can - // select them easily - async getProjectPriorities(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + // statuses - const projectId = this.getCurrentNodeParameter('projectId') as string; + async getIssueStatuses(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const statuses = await taigaApiRequest.call(this, 'GET', '/issue-statuses', {}, { project }) as LoadedResource[]; - const priorities = await taigaApiRequest.call(this, 'GET', '/priorities', {}, { project: projectId }); - for (const priority of priorities) { - const priorityName = priority.name; - const priorityId = priority.id; - returnData.push({ - name: priorityName, - value: priorityId, - }); - } - return returnData; + return toOptions(statuses); }, - // Get all the available severities to display them to user so that he can - // select them easily - async getProjectSeverities(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + async getTaskStatuses(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const statuses = await taigaApiRequest.call(this, 'GET', '/task-statuses', {}, { project }) as LoadedResource[]; - const projectId = this.getCurrentNodeParameter('projectId') as string; - - const severities = await taigaApiRequest.call(this, 'GET', '/severities', {}, { project: projectId }); - for (const severity of severities) { - const severityName = severity.name; - const severityId = severity.id; - returnData.push({ - name: severityName, - value: severityId, - }); - } - return returnData; + return toOptions(statuses); }, - // Get all the available milestones to display them to user so that he can - // select them easily - async getProjectMilestones(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + async getUserStoryStatuses(this: ILoadOptionsFunctions): Promise { + const project = this.getCurrentNodeParameter('projectId') as string; + const statuses = await taigaApiRequest.call(this, 'GET', '/userstory-statuses', {}, { project }) as LoadedResource[]; - const projectId = this.getCurrentNodeParameter('projectId') as string; - - const milestones = await taigaApiRequest.call(this, 'GET', '/milestones', {}, { project: projectId }); - for (const milestone of milestones) { - const milestoneName = milestone.name; - const milestoneId = milestone.id; - returnData.push({ - name: milestoneName, - value: milestoneId, - }); - } - return returnData; - }, - - // Get all the available projects to display them to user so that he can - // select them easily - async getUserProjects(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - - const { id } = await taigaApiRequest.call(this, 'GET', '/users/me'); - - const projects = await taigaApiRequest.call(this, 'GET', '/projects', {}, { member: id }); - for (const project of projects) { - const projectName = project.name; - const projectId = project.id; - returnData.push({ - name: projectName, - value: projectId, - }); - } - return returnData; + return toOptions(statuses); }, }, }; @@ -238,90 +187,365 @@ export class Taiga implements INodeType { async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; + + const resource = this.getNodeParameter('resource', 0) as Resource; + const operation = this.getNodeParameter('operation', 0) as Operation; + let responseData; - const resource = this.getNodeParameter('resource', 0) as string; - const operation = this.getNodeParameter('operation', 0) as string; - - const qs: IDataObject = {}; - for (let i = 0; i < items.length; i++) { - if (resource === 'issue') { - if (operation === 'create') { - const projectId = this.getNodeParameter('projectId', i) as number; - const subject = this.getNodeParameter('subject', i) as string; + try { - const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + if (resource === 'epic') { - const body: IDataObject = { - project: projectId, - subject, - }; + // ********************************************************************** + // epic + // ********************************************************************** - Object.assign(body, additionalFields); + if (operation === 'create') { + + // ---------------------------------------- + // epic: create + // ---------------------------------------- + + const body = { + project: this.getNodeParameter('projectId', i), + subject: this.getNodeParameter('subject', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await taigaApiRequest.call(this, 'POST', '/epics', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // epic: delete + // ---------------------------------------- + + const epicId = this.getNodeParameter('epicId', i); + + responseData = await taigaApiRequest.call(this, 'DELETE', `/epics/${epicId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // epic: get + // ---------------------------------------- + + const epicId = this.getNodeParameter('epicId', i); + + responseData = await taigaApiRequest.call(this, 'GET', `/epics/${epicId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // epic: getAll + // ---------------------------------------- + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + responseData = await handleListing.call(this, 'GET', '/epics', {}, qs, i); + + } else if (operation === 'update') { + + // ---------------------------------------- + // epic: update + // ---------------------------------------- + + const body = {} as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const epicId = this.getNodeParameter('epicId', i); + body.version = await getVersionForUpdate.call(this, `/epics/${epicId}`); + + responseData = await taigaApiRequest.call(this, 'PATCH', `/epics/${epicId}`, body); - if (body.tags) { - body.tags = (body.tags as string).split(',') as string[]; } - responseData = await taigaApiRequest.call(this, 'POST', '/issues', body); - } + } else if (resource === 'issue') { - if (operation === 'update') { + // ********************************************************************** + // issue + // ********************************************************************** - const issueId = this.getNodeParameter('issueId', i) as string; + if (operation === 'create') { - const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + // ---------------------------------------- + // issue: create + // ---------------------------------------- - const body: IDataObject = {}; + const body = { + project: this.getNodeParameter('projectId', i), + subject: this.getNodeParameter('subject', i), + } as IDataObject; - Object.assign(body, updateFields); + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await taigaApiRequest.call(this, 'POST', '/issues', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // issue: delete + // ---------------------------------------- + + const issueId = this.getNodeParameter('issueId', i); + + responseData = await taigaApiRequest.call(this, 'DELETE', `/issues/${issueId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // issue: get + // ---------------------------------------- + + const issueId = this.getNodeParameter('issueId', i); + + responseData = await taigaApiRequest.call(this, 'GET', `/issues/${issueId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // issue: getAll + // ---------------------------------------- + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + responseData = await handleListing.call(this, 'GET', '/issues', {}, qs, i); + + } else if (operation === 'update') { + + // ---------------------------------------- + // issue: update + // ---------------------------------------- + + const body = {} as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const issueId = this.getNodeParameter('issueId', i); + body.version = await getVersionForUpdate.call(this, `/issues/${issueId}`); + + responseData = await taigaApiRequest.call(this, 'PATCH', `/issues/${issueId}`, body); - if (body.tags) { - body.tags = (body.tags as string).split(',') as string[]; } - const { version } = await taigaApiRequest.call(this, 'GET', `/issues/${issueId}`); + } else if (resource === 'task') { - body.version = version; + // ********************************************************************** + // task + // ********************************************************************** - responseData = await taigaApiRequest.call(this, 'PATCH', `/issues/${issueId}`, body); - } + if (operation === 'create') { - if (operation === 'delete') { - const issueId = this.getNodeParameter('issueId', i) as string; - responseData = await taigaApiRequest.call(this, 'DELETE', `/issues/${issueId}`); - responseData = { success: true }; - } + // ---------------------------------------- + // task: create + // ---------------------------------------- - if (operation === 'get') { - const issueId = this.getNodeParameter('issueId', i) as string; - responseData = await taigaApiRequest.call(this, 'GET', `/issues/${issueId}`); - } + const body = { + project: this.getNodeParameter('projectId', i), + subject: this.getNodeParameter('subject', i), + } as IDataObject; - if (operation === 'getAll') { + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; - const projectId = this.getNodeParameter('projectId', i) as number; - const returnAll = this.getNodeParameter('returnAll', i) as boolean; + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } - qs.project = projectId; + responseData = await taigaApiRequest.call(this, 'POST', '/tasks', body); - if (returnAll === true) { - responseData = await taigaApiRequestAllItems.call(this, 'GET', '/issues', {}, qs); + } else if (operation === 'delete') { + + // ---------------------------------------- + // task: delete + // ---------------------------------------- + + const taskId = this.getNodeParameter('taskId', i); + + responseData = await taigaApiRequest.call(this, 'DELETE', `/tasks/${taskId}`); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // task: get + // ---------------------------------------- + + const taskId = this.getNodeParameter('taskId', i); + + responseData = await taigaApiRequest.call(this, 'GET', `/tasks/${taskId}`); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // task: getAll + // ---------------------------------------- + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + responseData = await handleListing.call(this, 'GET', '/tasks', {}, qs, i); + + } else if (operation === 'update') { + + // ---------------------------------------- + // task: update + // ---------------------------------------- + + const body = {} as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const taskId = this.getNodeParameter('taskId', i); + body.version = await getVersionForUpdate.call(this, `/tasks/${taskId}`); + + responseData = await taigaApiRequest.call(this, 'PATCH', `/tasks/${taskId}`, body); - } else { - qs.limit = this.getNodeParameter('limit', i) as number; - responseData = await taigaApiRequestAllItems.call(this, 'GET', '/issues', {}, qs); - responseData = responseData.splice(0, qs.limit); } + + } else if (resource === 'userStory') { + + // ********************************************************************** + // userStory + // ********************************************************************** + + if (operation === 'create') { + + // ---------------------------------------- + // userStory: create + // ---------------------------------------- + + const body = { + project: this.getNodeParameter('projectId', i), + subject: this.getNodeParameter('subject', i), + } as IDataObject; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (Object.keys(additionalFields).length) { + Object.assign(body, additionalFields); + } + + responseData = await taigaApiRequest.call(this, 'POST', '/userstories', body); + + } else if (operation === 'delete') { + + // ---------------------------------------- + // userStory: delete + // ---------------------------------------- + + const userStoryId = this.getNodeParameter('userStoryId', i); + + const endpoint = `/userstories/${userStoryId}`; + responseData = await taigaApiRequest.call(this, 'DELETE', endpoint); + responseData = { success: true }; + + } else if (operation === 'get') { + + // ---------------------------------------- + // userStory: get + // ---------------------------------------- + + const userStoryId = this.getNodeParameter('userStoryId', i); + + const endpoint = `/userstories/${userStoryId}`; + responseData = await taigaApiRequest.call(this, 'GET', endpoint); + + } else if (operation === 'getAll') { + + // ---------------------------------------- + // userStory: getAll + // ---------------------------------------- + + const qs = {} as IDataObject; + const filters = this.getNodeParameter('filters', i) as IDataObject; + + if (Object.keys(filters).length) { + Object.assign(qs, filters); + } + + responseData = await handleListing.call(this, 'GET', '/userstories', {}, qs, i); + + } else if (operation === 'update') { + + // ---------------------------------------- + // userStory: update + // ---------------------------------------- + + const body = {} as IDataObject; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (Object.keys(updateFields).length) { + Object.assign(body, updateFields); + } else { + throwOnEmptyUpdate.call(this, resource); + } + + const userStoryId = this.getNodeParameter('userStoryId', i); + body.version = await getVersionForUpdate.call(this, `/userstories/${userStoryId}`); + + responseData = await taigaApiRequest.call(this, 'PATCH', `/userstories/${userStoryId}`, body); + + } + } + + } catch (error) { + if (this.continueOnFail()) { + returnData.push({ error: error.message }); + continue; + } + + throw error; } - if (Array.isArray(responseData)) { - returnData.push.apply(returnData, responseData as IDataObject[]); - } else { - returnData.push(responseData as IDataObject); - } + + Array.isArray(responseData) + ? returnData.push(...responseData) + : returnData.push(responseData); + } return [this.helpers.returnJsonArray(returnData)]; diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.json b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.json index 2507764e3d..2ff5e5ab94 100644 --- a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.json +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.json @@ -3,6 +3,7 @@ "nodeVersion": "1.0", "codexVersion": "1.0", "categories": [ + "Development", "Productivity" ], "resources": { @@ -17,4 +18,4 @@ } ] } -} \ No newline at end of file +} diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts index 6598b1bac2..a01ff66b4c 100644 --- a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts @@ -26,7 +26,7 @@ export class TaigaTrigger implements INodeType { description: INodeTypeDescription = { displayName: 'Taiga Trigger', name: 'taigaTrigger', - icon: 'file:taiga.png', + icon: 'file:taiga.svg', group: ['trigger'], version: 1, subtitle: '={{"project:" + $parameter["projectSlug"]}}', @@ -39,25 +39,7 @@ export class TaigaTrigger implements INodeType { outputs: ['main'], credentials: [ { - name: 'taigaCloudApi', - displayOptions: { - show: { - version: [ - 'cloud', - ], - }, - }, - required: true, - }, - { - name: 'taigaServerApi', - displayOptions: { - show: { - version: [ - 'server', - ], - }, - }, + name: 'taigaApi', required: true, }, ], @@ -70,22 +52,6 @@ export class TaigaTrigger implements INodeType { }, ], properties: [ - { - displayName: 'Taiga Version', - name: 'version', - type: 'options', - options: [ - { - name: 'Cloud', - value: 'cloud', - }, - { - name: 'Server (Self Hosted)', - value: 'server', - }, - ], - default: 'cloud', - }, { displayName: 'Project ID', name: 'projectId', @@ -146,15 +112,7 @@ export class TaigaTrigger implements INodeType { return false; }, async create(this: IHookFunctions): Promise { - const version = this.getNodeParameter('version') as string; - - let credentials; - - if (version === 'server') { - credentials = this.getCredentials('taigaServerApi') as ICredentialDataDecryptedObject; - } else { - credentials = this.getCredentials('taigaCloudApi') as ICredentialDataDecryptedObject; - } + const credentials = this.getCredentials('taigaApi') as ICredentialDataDecryptedObject; const webhookUrl = this.getNodeWebhookUrl('default') as string; diff --git a/packages/nodes-base/nodes/Taiga/descriptions/EpicDescription.ts b/packages/nodes-base/nodes/Taiga/descriptions/EpicDescription.ts new file mode 100644 index 0000000000..69ab59ec5c --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/descriptions/EpicDescription.ts @@ -0,0 +1,429 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const epicOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'epic', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an epic', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an epic', + }, + { + name: 'Get', + value: 'get', + description: 'Get an epic', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all epics', + }, + { + name: 'Update', + value: 'update', + description: 'Update an epic', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const epicFields = [ + // ---------------------------------------- + // epic: create + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the epic belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to assign the epic to', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the epic is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Color', + name: 'color', + type: 'color', + default: '0000FF', + description: 'Color code in hexadecimal notation', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the issue is blocked', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + ], + }, + + // ---------------------------------------- + // epic: delete + // ---------------------------------------- + { + displayName: 'Epic ID', + name: 'epicId', + description: 'ID of the epic to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // epic: get + // ---------------------------------------- + { + displayName: 'Epic ID', + name: 'epicId', + description: 'ID of the epic to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // epic: getAll + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the epic belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'getAll', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user whom the epic is assigned to', + }, + { + displayName: 'Is Closed', + name: 'statusIsClosed', + description: 'Whether the epic is closed', + type: 'boolean', + default: false, + }, + ], + }, + + // ---------------------------------------- + // epic: update + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + description: 'ID of the project to set the epic to', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Epic ID', + name: 'epicId', + description: 'ID of the epic to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'epic', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to whom the epic is assigned', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the epic is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Color', + name: 'color', + type: 'color', + default: '0000FF', + description: 'Color code in hexadecimal notation', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the epic is blocked', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/descriptions/IssueDescription.ts b/packages/nodes-base/nodes/Taiga/descriptions/IssueDescription.ts new file mode 100644 index 0000000000..697dc9e4e4 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/descriptions/IssueDescription.ts @@ -0,0 +1,663 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const issueOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'issue', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an issue', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete an issue', + }, + { + name: 'Get', + value: 'get', + description: 'Get an issue', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all issues', + }, + { + name: 'Update', + value: 'update', + description: 'Update an issue', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const issueFields = [ + // ---------------------------------------- + // issue: create + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the issue belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to whom the issue is assigned', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the issue is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the issue is blocked', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the issue', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getPriorities', + }, + default: '', + }, + { + displayName: 'Severity', + name: 'severity', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getSeverities', + }, + default: '', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getIssueStatuses', + }, + default: '', + description: 'ID of the status of the issue', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + }, + ], + }, + + // ---------------------------------------- + // issue: delete + // ---------------------------------------- + { + displayName: 'Issue ID', + name: 'issueId', + description: 'ID of the issue to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // issue: get + // ---------------------------------------- + { + displayName: 'Issue ID', + name: 'issueId', + description: 'ID of the issue to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // issue: getAll + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the issue belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + description: 'ID of the user to assign the issue to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + { + displayName: 'Order By', + name: 'orderBy', + description: 'Field to order the issues by', + type: 'options', + options: [ + { + name: 'Assigned To', + value: 'assigned_to', + }, + { + name: 'Created Date', + value: 'created_date', + }, + { + name: 'Modified Date', + value: 'modified_date', + }, + { + name: 'Owner', + value: 'owner', + }, + { + name: 'Priority', + value: 'priority', + }, + { + name: 'Severity', + value: 'severity', + }, + { + name: 'Status', + value: 'status', + }, + { + name: 'Subject', + value: 'subject', + }, + { + name: 'Type', + value: 'type', + }, + ], + default: 'assigned_to', + }, + { + displayName: 'Owner', + name: 'owner', + description: 'ID of the owner of the issue', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getPriorities', + }, + default: '', + }, + { + displayName: 'Role', + name: 'role', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getRoles', + }, + default: '', + }, + { + displayName: 'Severity', + name: 'severity', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getSeverities', + }, + default: '', + }, + { + displayName: 'Status', + name: 'status', + description: 'ID of the status of the issue', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getIssueStatuses', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + }, + ], + }, + + // ---------------------------------------- + // issue: update + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + description: 'ID of the project to set the issue to', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Issue ID', + name: 'issueId', + description: 'ID of the issue to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user whom the issue is assigned to', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the issue is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the issue is blocked', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the issue', + }, + { + displayName: 'Priority', + name: 'priority', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getPriorities', + }, + default: '', + }, + { + displayName: 'Severity', + name: 'severity', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getSeverities', + }, + default: '', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getIssueStatuses', + }, + default: '', + description: 'ID of the status of the issue', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/descriptions/TaskDescription.ts b/packages/nodes-base/nodes/Taiga/descriptions/TaskDescription.ts new file mode 100644 index 0000000000..f2c90d2d32 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/descriptions/TaskDescription.ts @@ -0,0 +1,609 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const taskOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'task', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a task', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a task', + }, + { + name: 'Get', + value: 'get', + description: 'Get a task', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all tasks', + }, + { + name: 'Update', + value: 'update', + description: 'Update a task', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const taskFields = [ + // ---------------------------------------- + // task: create + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the task belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to whom the task is assigned', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the task is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the task is blocked', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the task', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTaskStatuses', + }, + default: '', + description: 'ID of the status of the task', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Taskboard Order', + name: 'taskboard_order', + type: 'number', + default: 1, + description: 'Order of the task in the taskboard', + typeOptions: { + minValue: 1, + }, + }, + { + displayName: 'User Story', + name: 'user_story', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStories', + }, + default: '', + description: 'ID of the user story of the task', + }, + { + displayName: 'User Story Order', + name: 'us_order', + type: 'number', + default: 1, + description: 'Order of the task in the user story', + typeOptions: { + minValue: 1, + }, + }, + ], + }, + + // ---------------------------------------- + // task: delete + // ---------------------------------------- + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // task: get + // ---------------------------------------- + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // task: getAll + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the task belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'getAll', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user whom the task is assigned to', + }, + { + displayName: 'Is Closed', + name: 'statusIsClosed', + description: 'Whether the task is closed', + type: 'boolean', + default: false, + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the task', + }, + { + displayName: 'Owner', + name: 'owner', + description: 'ID of the owner of the task', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + { + displayName: 'Role', + name: 'role', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getRoles', + }, + default: '', + }, + { + displayName: 'Status', + name: 'status', + description: 'ID of the status of the task', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTaskStatuses', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'User Story', + name: 'userStory', + description: 'ID of the user story to which the task belongs', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStories', + }, + default: '', + }, + ], + }, + + // ---------------------------------------- + // task: update + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to set the task to', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Task ID', + name: 'taskId', + description: 'ID of the task to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'task', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + description: 'ID of the user to assign the task to', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the task is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the task is blocked', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the task', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTaskStatuses', + }, + default: '', + description: 'ID of the status of the task', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'User Story', + name: 'user_story', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStories', + }, + default: '', + description: 'ID of the user story of the task', + }, + { + displayName: 'User Story Order', + name: 'us_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the task in the user story', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Taskboard Order', + name: 'taskboard_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the task in the taskboard', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/descriptions/UserStoryDescription.ts b/packages/nodes-base/nodes/Taiga/descriptions/UserStoryDescription.ts new file mode 100644 index 0000000000..9d046a934e --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/descriptions/UserStoryDescription.ts @@ -0,0 +1,621 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const userStoryOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a user story', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a user story', + }, + { + name: 'Get', + value: 'get', + description: 'Get a user story', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all user stories', + }, + { + name: 'Update', + value: 'update', + description: 'Update a user story', + }, + ], + default: 'create', + description: 'Operation to perform', + }, +] as INodeProperties[]; + +export const userStoryFields = [ + // ---------------------------------------- + // userStory: create + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the user story belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'create', + ], + }, + }, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to whom the user story is assigned', + }, + { + displayName: 'Backlog Order', + name: 'backlog_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the backlog', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the user story is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the user story is blocked', + }, + { + displayName: 'Kanban Order', + name: 'kanban_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the kanban', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the user story', + }, + { + displayName: 'Sprint Order', + name: 'sprint_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the milestone', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStoryStatuses', + }, + default: '', + description: 'ID of the status of the user story', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + }, + ], + }, + + // ---------------------------------------- + // userStory: delete + // ---------------------------------------- + { + displayName: 'User Story ID', + name: 'userStoryId', + description: 'ID of the user story to delete', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'delete', + ], + }, + }, + }, + + // ---------------------------------------- + // userStory: get + // ---------------------------------------- + { + displayName: 'User Story ID', + name: 'userStoryId', + description: 'ID of the user story to retrieve', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'get', + ], + }, + }, + }, + + // ---------------------------------------- + // userStory: getAll + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + description: 'ID of the project to which the user story belongs', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + default: false, + description: 'Whether to return all results or only up to a given limit', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'getAll', + ], + }, + }, + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + default: 50, + description: 'How many results to return', + typeOptions: { + minValue: 1, + }, + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'getAll', + ], + returnAll: [ + false, + ], + }, + }, + }, + { + displayName: 'Filters', + name: 'filters', + type: 'collection', + placeholder: 'Add Filter', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'getAll', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + description: 'ID of the user whom the user story is assigned to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + }, + { + displayName: 'Epic', + name: 'epic', + description: 'ID of the epic to which the user story belongs', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getEpics', + }, + default: '', + }, + { + displayName: 'Is Closed', + name: 'statusIsClosed', + description: 'Whether the user story is closed', + type: 'boolean', + default: false, + }, + { + displayName: 'Is Archived', + name: 'statusIsArchived', + description: 'Whether the user story has been archived', + type: 'boolean', + default: false, + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the user story', + }, + { + displayName: 'Role', + name: 'role', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getRoles', + }, + default: '', + }, + { + displayName: 'Status', + name: 'status', + description: 'ID of the status of the user story', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStoryStatuses', + }, + default: '', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + ], + }, + + // ---------------------------------------- + // userStory: update + // ---------------------------------------- + { + displayName: 'Project ID', + name: 'projectId', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + description: 'ID of the project to set the user story to', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'User Story ID', + name: 'userStoryId', + description: 'ID of the user story to update', + type: 'string', + required: true, + default: '', + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'update', + ], + }, + }, + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'userStory', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned To', + name: 'assigned_to', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUsers', + }, + default: '', + description: 'ID of the user to assign the the user story to', + }, + { + displayName: 'Backlog Order', + name: 'backlog_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the backlog', + }, + { + displayName: 'Blocked Note', + name: 'blocked_note', + type: 'string', + default: '', + description: 'Reason why the user story is blocked. Requires "Is Blocked" toggle to be enabled', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + }, + { + displayName: 'Is Blocked', + name: 'is_blocked', + type: 'boolean', + default: false, + description: 'Whether the user story is blocked', + }, + { + displayName: 'Kanban Order', + name: 'kanban_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the kanban', + }, + { + displayName: 'Milestone (Sprint)', + name: 'milestone', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getMilestones', + }, + default: '', + description: 'ID of the milestone of the user story', + }, + { + displayName: 'Subject', + name: 'subject', + type: 'string', + default: '', + }, + { + displayName: 'Sprint Order', + name: 'sprint_order', + type: 'number', + default: 1, + typeOptions: { + minValue: 1, + }, + description: 'Order of the user story in the milestone', + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getUserStoryStatuses', + }, + default: '', + description: 'ID of the status of the user story', + }, + { + displayName: 'Tags', + name: 'tags', + type: 'multiOptions', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTags', + }, + default: [], + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsDependsOn: [ + 'projectId', + ], + loadOptionsMethod: 'getTypes', + }, + default: '', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/descriptions/index.ts b/packages/nodes-base/nodes/Taiga/descriptions/index.ts new file mode 100644 index 0000000000..d0c74bae4d --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/descriptions/index.ts @@ -0,0 +1,4 @@ +export * from './EpicDescription'; +export * from './IssueDescription'; +export * from './TaskDescription'; +export * from './UserStoryDescription'; diff --git a/packages/nodes-base/nodes/Taiga/issueOperationFields.ts b/packages/nodes-base/nodes/Taiga/issueOperationFields.ts deleted file mode 100644 index 1ecf033414..0000000000 --- a/packages/nodes-base/nodes/Taiga/issueOperationFields.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { - INodeProperties, -} from 'n8n-workflow'; - -export const issueOperationFields = [ - { - displayName: 'Project ID', - name: 'projectId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getUserProjects', - }, - displayOptions: { - show: { - resource: [ - 'issue', - ], - operation: [ - 'create', - 'getAll', - 'update', - ], - }, - }, - default: '', - description: 'The project ID.', - required: true, - }, - { - displayName: 'Subject', - name: 'subject', - type: 'string', - displayOptions: { - show: { - resource: [ - 'issue', - ], - operation: [ - 'create', - ], - }, - }, - default: '', - required: true, - }, - { - displayName: 'Additional Fields', - name: 'additionalFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'issue', - ], - operation: [ - 'create', - ], - }, - }, - options: [ - { - displayName: 'Assigned To', - name: 'assigned_to', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectId', - ], - loadOptionsMethod: 'getProjectUsers', - }, - default: '', - description: 'User id to you want assign the issue to', - }, - { - displayName: 'Blocked Note', - name: 'blocked_note', - type: 'string', - default: '', - description: 'Reason why the issue is blocked', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - }, - { - displayName: 'Is Blocked', - name: 'is_blocked', - type: 'boolean', - default: false, - }, - { - displayName: 'Is Closed', - name: 'is_closed', - type: 'boolean', - default: false, - }, - { - displayName: 'Milestone ID', - name: 'milestone', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectMilestones', - }, - default: '', - }, - { - displayName: 'Priority ID', - name: 'priority', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectPriorities', - }, - default: '', - }, - { - displayName: 'Severity ID', - name: 'severity', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectSeverities', - }, - default: '', - }, - { - displayName: 'Status ID', - name: 'status', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getStatuses', - }, - default: '', - }, - { - displayName: 'Tags', - name: 'tags', - type: 'string', - description: 'Tags separated by comma.', - default: '', - placeholder: 'product, sales', - }, - { - displayName: 'Type ID', - name: 'type', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getTypes', - }, - default: '', - }, - ], - }, - { - displayName: 'Issue ID', - name: 'issueId', - type: 'string', - displayOptions: { - show: { - resource: [ - 'issue', - ], - operation: [ - 'update', - 'delete', - 'get', - ], - }, - }, - default: '', - required: true, - }, - { - displayName: 'Update Fields', - name: 'updateFields', - type: 'collection', - placeholder: 'Add Field', - default: {}, - displayOptions: { - show: { - resource: [ - 'issue', - ], - operation: [ - 'update', - ], - }, - }, - options: [ - { - displayName: 'Assigned To', - name: 'assigned_to', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectId', - ], - loadOptionsMethod: 'getProjectUsers', - }, - default: '', - description: 'User id to you want assign the issue to', - }, - { - displayName: 'Blocked Note', - name: 'blocked_note', - type: 'string', - default: '', - description: 'Reason why the issue is blocked', - }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - }, - { - displayName: 'Is Blocked', - name: 'is_blocked', - type: 'boolean', - default: false, - }, - { - displayName: 'Is Closed', - name: 'is_closed', - type: 'boolean', - default: false, - }, - { - displayName: 'Milestone ID', - name: 'milestone', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectMilestones', - }, - default: '', - }, - { - displayName: 'Priority ID', - name: 'priority', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectPriorities', - }, - default: '', - }, - { - displayName: 'Severity ID', - name: 'severity', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getProjectSeverities', - }, - default: '', - }, - { - displayName: 'Status ID', - name: 'status', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getStatuses', - }, - default: '', - }, - { - displayName: 'Subject', - name: 'subject', - type: 'string', - default: '', - }, - { - displayName: 'Tags', - name: 'tags', - type: 'string', - description: 'Tags separated by comma.', - default: '', - placeholder: 'product, sales', - }, - { - displayName: 'Type ID', - name: 'type', - type: 'options', - typeOptions: { - loadOptionsDependsOn: [ - 'projectSlug', - ], - loadOptionsMethod: 'getTypes', - }, - default: '', - }, - ], - }, - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'issue', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'issue', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, - -] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Taiga/taiga.png b/packages/nodes-base/nodes/Taiga/taiga.png deleted file mode 100644 index 3d7481cbaa..0000000000 Binary files a/packages/nodes-base/nodes/Taiga/taiga.png and /dev/null differ diff --git a/packages/nodes-base/nodes/Taiga/taiga.svg b/packages/nodes-base/nodes/Taiga/taiga.svg new file mode 100644 index 0000000000..913a5d31df --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/taiga.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/nodes-base/nodes/Taiga/types.d.ts b/packages/nodes-base/nodes/Taiga/types.d.ts new file mode 100644 index 0000000000..4eafdbbe93 --- /dev/null +++ b/packages/nodes-base/nodes/Taiga/types.d.ts @@ -0,0 +1,24 @@ +type Resource = 'epic' | 'issue' | 'task' | 'userStory'; + +type Operation = 'create' | 'delete' | 'update' | 'get' | 'getAll' + +type LoadedResource = { + id: string; + name: string; +}; + +type LoadedUser = { + id: string; + full_name_display: string; +}; + +type LoadedUserStory = { + id: string; + subject: string; +}; + +type LoadedEpic = LoadedUserStory; + +type LoadedTags = { + [tagName: string]: string | null; // hex color +} diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index b3efce352b..d027c87478 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -244,8 +244,7 @@ "dist/credentials/StrapiApi.credentials.js", "dist/credentials/SurveyMonkeyApi.credentials.js", "dist/credentials/SurveyMonkeyOAuth2Api.credentials.js", - "dist/credentials/TaigaCloudApi.credentials.js", - "dist/credentials/TaigaServerApi.credentials.js", + "dist/credentials/TaigaApi.credentials.js", "dist/credentials/TapfiliateApi.credentials.js", "dist/credentials/TelegramApi.credentials.js", "dist/credentials/TheHiveApi.credentials.js",