diff --git a/packages/nodes-base/nodes/Linear/CommentDescription.ts b/packages/nodes-base/nodes/Linear/CommentDescription.ts new file mode 100644 index 0000000000..90f4b2e8bb --- /dev/null +++ b/packages/nodes-base/nodes/Linear/CommentDescription.ts @@ -0,0 +1,78 @@ +import type { INodeProperties } from 'n8n-workflow'; + +export const commentOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['comment'], + }, + }, + options: [ + { + name: 'Add Comment', + value: 'addComment', + description: 'Add a comment to an issue', + action: 'Add a comment to an issue', + }, + ], + default: 'addComment', + }, +]; + +export const commentFields: INodeProperties[] = [ + /* -------------------------------------------------------------------------- */ + /* comment:addComment */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Issue ID', + name: 'issueId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: ['comment'], + operation: ['addComment'], + }, + }, + default: '', + }, + { + displayName: 'Comment', + name: 'comment', + type: 'string', + required: true, + displayOptions: { + show: { + resource: ['comment'], + operation: ['addComment'], + }, + }, + default: '', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['comment'], + operation: ['addComment'], + }, + }, + options: [ + { + displayName: 'Parent Comment ID', + name: 'parentId', + type: 'string', + description: 'ID of the parent comment if this is a reply', + default: '', + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Linear/IssueDescription.ts b/packages/nodes-base/nodes/Linear/IssueDescription.ts index 5b0eb94b0c..f37c3408af 100644 --- a/packages/nodes-base/nodes/Linear/IssueDescription.ts +++ b/packages/nodes-base/nodes/Linear/IssueDescription.ts @@ -12,6 +12,12 @@ export const issueOperations: INodeProperties[] = [ }, }, options: [ + { + name: 'Add Link', + value: 'addLink', + description: 'Add a link to an issue', + action: 'Add a link to an issue', + }, { name: 'Create', value: 'create', @@ -164,7 +170,7 @@ export const issueFields: INodeProperties[] = [ displayOptions: { show: { resource: ['issue'], - operation: ['get', 'delete'], + operation: ['addLink', 'get', 'delete'], }, }, default: '', @@ -307,4 +313,20 @@ export const issueFields: INodeProperties[] = [ }, ], }, + /* -------------------------------------------------------------------------- */ + /* issue:addLink */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Link', + name: 'link', + type: 'string', + required: true, + displayOptions: { + show: { + resource: ['issue'], + operation: ['addLink'], + }, + }, + default: '', + }, ]; diff --git a/packages/nodes-base/nodes/Linear/Linear.node.ts b/packages/nodes-base/nodes/Linear/Linear.node.ts index caaf1b52c3..560b79864b 100644 --- a/packages/nodes-base/nodes/Linear/Linear.node.ts +++ b/packages/nodes-base/nodes/Linear/Linear.node.ts @@ -14,6 +14,7 @@ import { NodeConnectionTypes, } from 'n8n-workflow'; +import { commentFields, commentOperations } from './CommentDescription'; import { linearApiRequest, linearApiRequestAllItems, @@ -85,6 +86,10 @@ export class Linear implements INodeType { type: 'options', noDataExpression: true, options: [ + { + name: 'Comment', + value: 'comment', + }, { name: 'Issue', value: 'issue', @@ -92,6 +97,8 @@ export class Linear implements INodeType { ], default: 'issue', }, + ...commentOperations, + ...commentFields, ...issueOperations, ...issueFields, ], @@ -289,6 +296,39 @@ export class Linear implements INodeType { responseData = await linearApiRequest.call(this, body); responseData = responseData?.data?.issueUpdate?.issue; } + if (operation === 'addLink') { + const issueId = this.getNodeParameter('issueId', i) as string; + const body: IGraphqlBody = { + query: query.addIssueLink(), + variables: { + issueId, + url: this.getNodeParameter('link', i), + }, + }; + + responseData = await linearApiRequest.call(this, body); + responseData = responseData?.data?.attachmentLinkURL; + } + } else if (resource === 'comment') { + if (operation === 'addComment') { + const issueId = this.getNodeParameter('issueId', i) as string; + const body = this.getNodeParameter('comment', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + const requestBody: IGraphqlBody = { + query: query.addComment(), + variables: { + issueId, + body, + }, + }; + + if (additionalFields.parentId && (additionalFields.parentId as string).trim() !== '') { + requestBody.variables.parentId = additionalFields.parentId as string; + } + + responseData = await linearApiRequest.call(this, requestBody); + responseData = responseData?.data?.commentCreate; + } } const executionData = this.helpers.constructExecutionMetaData( diff --git a/packages/nodes-base/nodes/Linear/Queries.ts b/packages/nodes-base/nodes/Linear/Queries.ts index 8b6926f0bf..c28d9243da 100644 --- a/packages/nodes-base/nodes/Linear/Queries.ts +++ b/packages/nodes-base/nodes/Linear/Queries.ts @@ -218,4 +218,21 @@ export const query = { } }`; }, + addComment() { + return `mutation CommentCreate ($issueId: String!, $body: String!, $parentId: String) { + commentCreate(input: {issueId: $issueId, body: $body, parentId: $parentId}) { + success + comment { + id + } + } + }`; + }, + addIssueLink() { + return `mutation AttachmentLinkURL($url: String!, $issueId: String!) { + attachmentLinkURL(url: $url, issueId: $issueId) { + success + } + }`; + }, }; diff --git a/packages/nodes-base/nodes/Linear/test/GenericFunctions.test.ts b/packages/nodes-base/nodes/Linear/test/GenericFunctions.test.ts index b49b59133a..2e3c7b019d 100644 --- a/packages/nodes-base/nodes/Linear/test/GenericFunctions.test.ts +++ b/packages/nodes-base/nodes/Linear/test/GenericFunctions.test.ts @@ -111,25 +111,66 @@ describe('Linear -> GenericFunctions', () => { }); describe('sort', () => { - it('should sort objects by name in ascending order', () => { - const array = [{ name: 'banana' }, { name: 'apple' }, { name: 'cherry' }]; + it('should correctly sort objects by name in ascending order', () => { + // Test data + const items = [ + { name: 'Banana', id: 3 }, + { name: 'apple', id: 2 }, + { name: 'Cherry', id: 1 }, + { name: 'date', id: 4 }, + ]; - const sortedArray = array.sort(sort); + // Execute sort + const sorted = [...items].sort(sort); - expect(sortedArray).toEqual([{ name: 'apple' }, { name: 'banana' }, { name: 'cherry' }]); + // Verify sorted order (case-insensitive) + expect(sorted.map((item) => item.id)).toEqual([2, 3, 1, 4]); + expect(sorted.map((item) => item.name)).toEqual(['apple', 'Banana', 'Cherry', 'date']); }); - it('should handle case insensitivity', () => { - const array = [{ name: 'Banana' }, { name: 'apple' }, { name: 'cherry' }]; + it('should treat uppercase and lowercase names as equal', () => { + const a = { name: 'apple' }; + const b = { name: 'APPLE' }; - const sortedArray = array.sort(sort); - - expect(sortedArray).toEqual([{ name: 'apple' }, { name: 'Banana' }, { name: 'cherry' }]); + expect(sort(a, b)).toBe(0); + expect(sort(b, a)).toBe(0); }); - it('should return 0 for objects with the same name', () => { - const result = sort({ name: 'apple' }, { name: 'apple' }); - expect(result).toBe(0); + it('should return -1 when first name comes before second name alphabetically', () => { + const a = { name: 'apple' }; + const b = { name: 'banana' }; + + expect(sort(a, b)).toBe(-1); + }); + + it('should return 1 when first name comes after second name alphabetically', () => { + const a = { name: 'cherry' }; + const b = { name: 'banana' }; + + expect(sort(a, b)).toBe(1); + }); + + it('should return 0 for identical names', () => { + const a = { name: 'apple' }; + const b = { name: 'apple' }; + + expect(sort(a, b)).toBe(0); + }); + + it('should handle mixed case properly', () => { + // Test data + const items = [ + { name: 'abc', id: 1 }, + { name: 'ABC', id: 2 }, + { name: 'Abc', id: 3 }, + { name: 'aBC', id: 4 }, + ]; + + // They should all be considered equal in terms of sorting + const sorted = [...items].sort(sort); + + // Original order should be maintained for equal values + expect(sorted.map((item) => item.id)).toEqual([1, 2, 3, 4]); }); }); }); diff --git a/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.json b/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.json new file mode 100644 index 0000000000..2c9fc32ea1 --- /dev/null +++ b/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.json @@ -0,0 +1,525 @@ +{ + "name": "Linear Test Workflow", + "nodes": [ + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [-80, 260], + "id": "4dbe018c-edec-46d3-b18d-f4517435e1f8", + "name": "When clicking ‘Execute workflow’" + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, -400], + "id": "512f2fec-df9f-46ac-8df3-e84fd352f7ff", + "name": "Create Comment - Output" + }, + { + "parameters": { + "resource": "comment", + "issueId": "test-17", + "comment": "test", + "additionalFields": {} + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, -400], + "id": "be60984b-28a5-4c5a-8926-87824300e63e", + "name": "Add Comment", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": { + "resource": "comment", + "issueId": "test-17", + "comment": "Add to parent", + "additionalFields": { + "parentId": "ff12069e-fac8-4b18-8455-cc6b29fa1e77" + } + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, -220], + "id": "328939ed-41ac-442e-819c-f470ce0986b4", + "name": "Add Comment - with parent", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, -220], + "id": "d28e9fe2-4a7b-4714-aa15-a6c6732c4e0d", + "name": "Create Comment with Parent" + }, + { + "parameters": { + "operation": "addLink", + "issueId": "test-17", + "link": "https://n8n.io" + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, -20], + "id": "6ec9fdbc-eed8-4e9a-850b-973f72f6f3a5", + "name": "Add link to issue", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, -20], + "id": "229e5b09-8867-44f5-a71c-da31d97ab1b5", + "name": "Add Link output" + }, + { + "parameters": { + "teamId": "0a2994c1-5d99-48aa-ab22-8b5ba4711ebc", + "title": "This is a test issue", + "additionalFields": { + "assigneeId": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "description": "test description", + "priorityId": 3, + "stateId": "65a87a3a-5729-4d82-96bf-badccbeb49af" + } + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, 160], + "id": "3262089e-902a-4922-b5e9-1bd2dae4915f", + "name": "Create Issue", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, 160], + "id": "3515db04-1fb9-4c15-904b-1a34c7bd63fb", + "name": "Create Issue Response" + }, + { + "parameters": { + "operation": "getAll", + "limit": 1 + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, 520], + "id": "912a5680-2d83-4d24-921b-f622cc8be0be", + "name": "Issue Get Many", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": { + "operation": "get", + "issueId": "test-18" + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, 340], + "id": "8604ce38-89aa-4793-bd2d-13c7d4fce215", + "name": "Get Issue", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, 520], + "id": "d01f72ce-4f5f-4a3f-9805-7975e67d041d", + "name": "Get Many Issues" + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, 340], + "id": "a2ad027f-8507-4488-84f5-f339ad826120", + "name": "Get Issue Output" + }, + { + "parameters": { + "operation": "update", + "issueId": "test-18", + "updateFields": { + "assigneeId": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "description": "New Description", + "priorityId": 3, + "stateId": "622493c0-f4ee-456d-af65-49a7611ede7a", + "teamId": "0a2994c1-5d99-48aa-ab22-8b5ba4711ebc", + "title": "New Title" + } + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, 700], + "id": "141d4f10-6812-40b4-a146-fa9cf929f86a", + "name": "Update Issue", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, 700], + "id": "b08bba99-9d96-43be-8875-c72c7b51f734", + "name": "Update Issue Response" + }, + { + "parameters": { + "operation": "delete", + "issueId": "test-18" + }, + "type": "n8n-nodes-base.linear", + "typeVersion": 1, + "position": [200, 880], + "id": "d30a2ff2-f8cd-4277-b5c1-6f2df7985872", + "name": "Delete Issue", + "credentials": { + "linearApi": { + "id": "6bJTI5tzXOk9m6cv", + "name": "86-88" + } + } + }, + { + "parameters": {}, + "type": "n8n-nodes-base.noOp", + "typeVersion": 1, + "position": [400, 880], + "id": "8769421e-01ea-41ee-9e35-432408879869", + "name": "Delete Issue Response" + } + ], + "pinData": { + "Create Comment - Output": [ + { + "json": { + "success": true, + "comment": { + "id": "ff12069e-fac8-4b18-8455-cc6b29fa1e77" + } + } + } + ], + "Create Comment with Parent": [ + { + "json": { + "success": true, + "comment": { + "id": "bd0e4d70-7964-4877-aa30-d81534027f44" + } + } + } + ], + "Add Link output": [ + { + "json": { + "success": true + } + } + ], + "Create Issue Response": [ + { + "json": { + "id": "3c7316e3-4224-424d-8cc8-1dd3b96764b8", + "identifier": "TEST-18", + "title": "This is a test issue", + "priority": 3, + "archivedAt": null, + "assignee": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "state": { + "id": "65a87a3a-5729-4d82-96bf-badccbeb49af", + "name": "Backlog" + }, + "createdAt": "2025-06-12T10:38:35.296Z", + "creator": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "description": "test description", + "dueDate": null, + "cycle": null + } + } + ], + "Get Issue Output": [ + { + "json": { + "id": "3c7316e3-4224-424d-8cc8-1dd3b96764b8", + "identifier": "TEST-18", + "title": "This is a test issue", + "priority": 3, + "archivedAt": null, + "assignee": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "state": { + "id": "65a87a3a-5729-4d82-96bf-badccbeb49af", + "name": "Backlog" + }, + "createdAt": "2025-06-12T10:38:35.296Z", + "creator": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "description": "test description", + "dueDate": null, + "cycle": null + } + } + ], + "Get Many Issues": [ + { + "json": { + "id": "3c7316e3-4224-424d-8cc8-1dd3b96764b8", + "identifier": "TEST-18", + "title": "This is a test issue", + "priority": 3, + "archivedAt": null, + "assignee": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "state": { + "id": "65a87a3a-5729-4d82-96bf-badccbeb49af", + "name": "Backlog" + }, + "createdAt": "2025-06-12T10:38:35.296Z", + "creator": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "description": "test description", + "dueDate": null, + "cycle": null + } + } + ], + "Update Issue Response": [ + { + "json": { + "id": "3c7316e3-4224-424d-8cc8-1dd3b96764b8", + "identifier": "TEST-18", + "title": "New Title", + "priority": 3, + "archivedAt": null, + "assignee": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "state": { + "id": "622493c0-f4ee-456d-af65-49a7611ede7a", + "name": "Canceled" + }, + "createdAt": "2025-06-12T10:38:35.296Z", + "creator": { + "id": "1c51f0c4-c552-4614-a534-8de1752ba7d7", + "displayName": "nathan" + }, + "description": "New Description", + "dueDate": null, + "cycle": null + } + } + ], + "Delete Issue Response": [ + { + "json": { + "success": true + } + } + ] + }, + "connections": { + "When clicking ‘Execute workflow’": { + "main": [ + [ + { + "node": "Add Comment", + "type": "main", + "index": 0 + }, + { + "node": "Add Comment - with parent", + "type": "main", + "index": 0 + }, + { + "node": "Add link to issue", + "type": "main", + "index": 0 + }, + { + "node": "Create Issue", + "type": "main", + "index": 0 + }, + { + "node": "Get Issue", + "type": "main", + "index": 0 + }, + { + "node": "Issue Get Many", + "type": "main", + "index": 0 + }, + { + "node": "Update Issue", + "type": "main", + "index": 0 + }, + { + "node": "Delete Issue", + "type": "main", + "index": 0 + } + ] + ] + }, + "Add Comment": { + "main": [ + [ + { + "node": "Create Comment - Output", + "type": "main", + "index": 0 + } + ] + ] + }, + "Add Comment - with parent": { + "main": [ + [ + { + "node": "Create Comment with Parent", + "type": "main", + "index": 0 + } + ] + ] + }, + "Add link to issue": { + "main": [ + [ + { + "node": "Add Link output", + "type": "main", + "index": 0 + } + ] + ] + }, + "Create Issue": { + "main": [ + [ + { + "node": "Create Issue Response", + "type": "main", + "index": 0 + } + ] + ] + }, + "Get Issue": { + "main": [ + [ + { + "node": "Get Issue Output", + "type": "main", + "index": 0 + } + ] + ] + }, + "Issue Get Many": { + "main": [ + [ + { + "node": "Get Many Issues", + "type": "main", + "index": 0 + } + ] + ] + }, + "Update Issue": { + "main": [ + [ + { + "node": "Update Issue Response", + "type": "main", + "index": 0 + } + ] + ] + }, + "Delete Issue": { + "main": [ + [ + { + "node": "Delete Issue Response", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "2cc012ac-e157-4f3e-97d8-15fb2da556ef", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "0fa937d34dcabeff4bd6480d3b42cc95edf3bc20e6810819086ef1ce2623639d" + }, + "id": "6FHfNEY4tcmoXVeg", + "tags": [] +} diff --git a/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.test.ts b/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.test.ts new file mode 100644 index 0000000000..f2fb07718e --- /dev/null +++ b/packages/nodes-base/nodes/Linear/test/workflow/Linear.workflow.test.ts @@ -0,0 +1,43 @@ +import { NodeTestHarness } from '@nodes-testing/node-test-harness'; +import nock from 'nock'; + +import { + addCommentRequest, + addCommentWithParentRequest, + addCommentLink, + issueCreateRequest, + getIssueRequest, + getManyIssuesRequest, + updateIssueRequest, + deleteIssueRequest, +} from './apiRequest'; +import { + commentCreateResponse, + commentCreateWithParentResponse, + attachmentLinkURLResponse, + issueCreateResponse, + getIssueResponse, + getManyIssueResponse, + issueUpdateResponse, + deleteIssueResponse, +} from './apiResponses'; + +describe('Linear', () => { + describe('Run Test Workflow', () => { + beforeAll(() => { + const mock = nock('https://api.linear.app'); + mock.post('/graphql', addCommentRequest).reply(200, commentCreateResponse); + mock.post('/graphql', addCommentLink).reply(200, attachmentLinkURLResponse); + mock + .post('/graphql', addCommentWithParentRequest) + .reply(200, commentCreateWithParentResponse); + mock.post('/graphql', issueCreateRequest).reply(200, issueCreateResponse); + mock.post('/graphql', getIssueRequest).reply(200, getIssueResponse); + mock.post('/graphql', getManyIssuesRequest).reply(200, getManyIssueResponse); + mock.post('/graphql', updateIssueRequest).reply(200, issueUpdateResponse); + mock.post('/graphql', deleteIssueRequest).reply(200, deleteIssueResponse); + }); + + new NodeTestHarness().setupTests(); + }); +}); diff --git a/packages/nodes-base/nodes/Linear/test/workflow/apiRequest.ts b/packages/nodes-base/nodes/Linear/test/workflow/apiRequest.ts new file mode 100644 index 0000000000..21b2ce474a --- /dev/null +++ b/packages/nodes-base/nodes/Linear/test/workflow/apiRequest.ts @@ -0,0 +1,245 @@ +export const addCommentRequest = { + query: `mutation CommentCreate ($issueId: String!, $body: String!, $parentId: String) { + commentCreate(input: {issueId: $issueId, body: $body, parentId: $parentId}) { + success + comment { + id + } + } + }`, + variables: { + issueId: 'test-17', + body: 'test', + }, +}; + +export const addCommentWithParentRequest = { + query: `mutation CommentCreate ($issueId: String!, $body: String!, $parentId: String) { + commentCreate(input: {issueId: $issueId, body: $body, parentId: $parentId}) { + success + comment { + id + } + } + }`, + variables: { + issueId: 'test-17', + body: 'Add to parent', + parentId: 'ff12069e-fac8-4b18-8455-cc6b29fa1e77', + }, +}; + +export const addCommentLink = { + query: `mutation AttachmentLinkURL($url: String!, $issueId: String!) { + attachmentLinkURL(url: $url, issueId: $issueId) { + success + } + }`, + variables: { + issueId: 'test-17', + url: 'https://n8n.io', + }, +}; + +export const issueCreateRequest = { + query: `mutation IssueCreate ( + $title: String!, + $teamId: String!, + $description: String, + $assigneeId: String, + $priorityId: Int, + $stateId: String){ + issueCreate( + input: { + title: $title + description: $description + teamId: $teamId + assigneeId: $assigneeId + priority: $priorityId + stateId: $stateId + } + ) { + success + issue { + id, + identifier, + title, + priority + archivedAt + assignee { + id + displayName + } + state { + id + name + } + createdAt + creator { + id + displayName + } + description + dueDate + cycle { + id + name + } + } + } + }`, + variables: { + teamId: '0a2994c1-5d99-48aa-ab22-8b5ba4711ebc', + title: 'This is a test issue', + assigneeId: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + description: 'test description', + priorityId: 3, + stateId: '65a87a3a-5729-4d82-96bf-badccbeb49af', + }, +}; + +export const getIssueRequest = { + query: `query Issue($issueId: String!) { + issue(id: $issueId) { + id, + identifier, + title, + priority, + archivedAt, + assignee { + id, + displayName + } + state { + id + name + } + createdAt + creator { + id + displayName + } + description + dueDate + cycle { + id + name + } + } + }`, + variables: { + issueId: 'test-18', + }, +}; + +export const getManyIssuesRequest = { + query: `query Issue ($first: Int, $after: String){ + issues (first: $first, after: $after){ + nodes { + id, + identifier, + title, + priority + archivedAt + assignee { + id + displayName + } + state { + id + name + } + createdAt + creator { + id + displayName + } + description + dueDate + cycle { + id + name + } + } + pageInfo { + hasNextPage + endCursor + } + } + }`, + variables: { + first: 1, + after: null, + }, +}; + +export const updateIssueRequest = { + query: `mutation IssueUpdate ( + $issueId: String!, + $title: String, + $teamId: String, + $description: String, + $assigneeId: String, + $priorityId: Int, + $stateId: String){ + issueUpdate( + id: $issueId, + input: { + title: $title + description: $description + teamId: $teamId + assigneeId: $assigneeId + priority: $priorityId + stateId: $stateId + } + ) { + success + issue { + id, + identifier, + title, + priority + archivedAt + assignee { + id + displayName + } + state { + id + name + } + createdAt + creator { + id + displayName + } + description + dueDate + cycle { + id + name + } + } + } + }`, + variables: { + issueId: 'test-18', + assigneeId: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + description: 'New Description', + priorityId: 3, + stateId: '622493c0-f4ee-456d-af65-49a7611ede7a', + teamId: '0a2994c1-5d99-48aa-ab22-8b5ba4711ebc', + title: 'New Title', + }, +}; + +export const deleteIssueRequest = { + query: `mutation IssueDelete ($issueId: String!) { + issueDelete(id: $issueId) { + success + } + }`, + variables: { + issueId: 'test-18', + }, +}; diff --git a/packages/nodes-base/nodes/Linear/test/workflow/apiResponses.ts b/packages/nodes-base/nodes/Linear/test/workflow/apiResponses.ts new file mode 100644 index 0000000000..378abb05e6 --- /dev/null +++ b/packages/nodes-base/nodes/Linear/test/workflow/apiResponses.ts @@ -0,0 +1,163 @@ +export const commentCreateResponse = { + data: { + commentCreate: { + success: true, + comment: { + id: 'ff12069e-fac8-4b18-8455-cc6b29fa1e77', + }, + }, + }, +}; + +export const commentCreateWithParentResponse = { + data: { + commentCreate: { + success: true, + comment: { + id: 'bd0e4d70-7964-4877-aa30-d81534027f44', + }, + }, + }, +}; + +export const attachmentLinkURLResponse = { + data: { + attachmentLinkURL: { + success: true, + }, + }, +}; + +export const issueCreateResponse = { + data: { + issueCreate: { + success: true, + issue: { + id: '3c7316e3-4224-424d-8cc8-1dd3b96764b8', + identifier: 'TEST-18', + title: 'This is a test issue', + priority: 3, + archivedAt: null, + assignee: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + state: { + id: '65a87a3a-5729-4d82-96bf-badccbeb49af', + name: 'Backlog', + }, + createdAt: '2025-06-12T10:38:35.296Z', + creator: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + description: 'test description', + dueDate: null, + cycle: null, + }, + }, + }, +}; + +export const getIssueResponse = { + data: { + issue: { + id: '3c7316e3-4224-424d-8cc8-1dd3b96764b8', + identifier: 'TEST-18', + title: 'This is a test issue', + priority: 3, + archivedAt: null, + assignee: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + state: { + id: '65a87a3a-5729-4d82-96bf-badccbeb49af', + name: 'Backlog', + }, + createdAt: '2025-06-12T10:38:35.296Z', + creator: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + description: 'test description', + dueDate: null, + cycle: null, + }, + }, +}; + +export const getManyIssueResponse = { + data: { + issues: { + nodes: [ + { + id: '3c7316e3-4224-424d-8cc8-1dd3b96764b8', + identifier: 'TEST-18', + title: 'This is a test issue', + priority: 3, + archivedAt: null, + assignee: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + state: { + id: '65a87a3a-5729-4d82-96bf-badccbeb49af', + name: 'Backlog', + }, + createdAt: '2025-06-12T10:38:35.296Z', + creator: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + description: 'test description', + dueDate: null, + cycle: null, + }, + ], + pageInfo: { + hasNextPage: true, + endCursor: '3c7316e3-4224-424d-8cc8-1dd3b96764b8', + }, + }, + }, +}; + +export const issueUpdateResponse = { + data: { + issueUpdate: { + success: true, + issue: { + id: '3c7316e3-4224-424d-8cc8-1dd3b96764b8', + identifier: 'TEST-18', + title: 'New Title', + priority: 3, + archivedAt: null, + assignee: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + state: { + id: '622493c0-f4ee-456d-af65-49a7611ede7a', + name: 'Canceled', + }, + createdAt: '2025-06-12T10:38:35.296Z', + creator: { + id: '1c51f0c4-c552-4614-a534-8de1752ba7d7', + displayName: 'nathan', + }, + description: 'New Description', + dueDate: null, + cycle: null, + }, + }, + }, +}; + +export const deleteIssueResponse = { + data: { + issueDelete: { + success: true, + }, + }, +};