diff --git a/packages/nodes-base/credentials/MondayComApi.credentials.ts b/packages/nodes-base/credentials/MondayComApi.credentials.ts index abd31133fd440..d82677b090828 100644 --- a/packages/nodes-base/credentials/MondayComApi.credentials.ts +++ b/packages/nodes-base/credentials/MondayComApi.credentials.ts @@ -1,4 +1,9 @@ -import type { ICredentialType, INodeProperties } from 'n8n-workflow'; +import type { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; export class MondayComApi implements ICredentialType { name = 'mondayComApi'; @@ -16,4 +21,27 @@ export class MondayComApi implements ICredentialType { default: '', }, ]; + + authenticate: IAuthenticateGeneric = { + type: 'generic', + properties: { + headers: { + Authorization: '=Bearer {{$credentials.apiToken}}', + }, + }, + }; + + test: ICredentialTestRequest = { + request: { + headers: { + 'API-Version': '2023-10', + 'Content-Type': 'application/json', + }, + baseURL: 'https://api.monday.com/v2', + method: 'POST', + body: JSON.stringify({ + query: 'query { me { name }}', + }), + }, + }; } diff --git a/packages/nodes-base/nodes/MondayCom/GenericFunctions.ts b/packages/nodes-base/nodes/MondayCom/GenericFunctions.ts index c9d39eb71e820..07f017ff1bf84 100644 --- a/packages/nodes-base/nodes/MondayCom/GenericFunctions.ts +++ b/packages/nodes-base/nodes/MondayCom/GenericFunctions.ts @@ -14,34 +14,31 @@ import get from 'lodash/get'; export async function mondayComApiRequest( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, - body: any = {}, option: IDataObject = {}, ): Promise { const authenticationMethod = this.getNodeParameter('authentication', 0) as string; - const endpoint = 'https://api.monday.com/v2/'; - let options: OptionsWithUri = { headers: { + 'API-Version': '2023-10', 'Content-Type': 'application/json', }, method: 'POST', body, - uri: endpoint, + uri: 'https://api.monday.com/v2/', json: true, }; + options = Object.assign({}, options, option); - try { - if (authenticationMethod === 'accessToken') { - const credentials = await this.getCredentials('mondayComApi'); - options.headers = { Authorization: `Bearer ${credentials.apiToken}` }; + try { + let credentialType = 'mondayComApi'; - return await this.helpers.request(options); - } else { - return await this.helpers.requestOAuth2.call(this, 'mondayComOAuth2Api', options); + if (authenticationMethod === 'oAuth2') { + credentialType = 'mondayComOAuth2Api'; } + return await this.helpers.requestWithAuthentication.call(this, credentialType, options); } catch (error) { throw new NodeApiError(this.getNode(), error as JsonObject); } @@ -50,7 +47,6 @@ export async function mondayComApiRequest( export async function mondayComApiRequestAllItems( this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, - body: any = {}, ): Promise { const returnData: IDataObject[] = []; @@ -66,3 +62,41 @@ export async function mondayComApiRequestAllItems( } while (get(responseData, propertyName).length > 0); return returnData; } + +export async function mondayComApiPaginatedRequest( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + itemsPath: string, + fieldsToReturn: string, + body: IDataObject = {}, +) { + const returnData: IDataObject[] = []; + + const initialResponse = await mondayComApiRequest.call(this, body); + const data = get(initialResponse, itemsPath) as IDataObject; + + if (data) { + returnData.push.apply(returnData, data.items as IDataObject[]); + + let cursor: null | string = data.cursor as string; + + while (cursor) { + const responseData = ( + (await mondayComApiRequest.call(this, { + query: `query ( $cursor: String!) { next_items_page (cursor: $cursor, limit: 100) { cursor items ${fieldsToReturn} } }`, + variables: { + cursor, + }, + })) as IDataObject + ).data as { next_items_page: { cursor: string; items: IDataObject[] } }; + + if (responseData && responseData.next_items_page) { + returnData.push.apply(returnData, responseData.next_items_page.items); + cursor = responseData.next_items_page.cursor; + } else { + cursor = null; + } + } + } + + return returnData; +} diff --git a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts index 1584e2fa693fb..2056616caa593 100644 --- a/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts +++ b/packages/nodes-base/nodes/MondayCom/MondayCom.node.ts @@ -10,7 +10,11 @@ import type { import { NodeOperationError } from 'n8n-workflow'; import { snakeCase } from 'change-case'; -import { mondayComApiRequest, mondayComApiRequestAllItems } from './GenericFunctions'; +import { + mondayComApiPaginatedRequest, + mondayComApiRequest, + mondayComApiRequestAllItems, +} from './GenericFunctions'; import { boardFields, boardOperations } from './BoardDescription'; @@ -155,18 +159,17 @@ export class MondayCom implements INodeType { // select them easily async getColumns(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const boardId = parseInt(this.getCurrentNodeParameter('boardId') as string, 10); + const boardId = this.getCurrentNodeParameter('boardId') as string; const body: IGraphqlBody = { - query: `query ($boardId: [Int]) { + query: `query ($boardId: [ID!]) { boards (ids: $boardId){ - columns() { + columns { id title } } }`, variables: { - page: 1, boardId, }, }; @@ -190,11 +193,11 @@ export class MondayCom implements INodeType { // select them easily async getGroups(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const boardId = parseInt(this.getCurrentNodeParameter('boardId') as string, 10); + const boardId = this.getCurrentNodeParameter('boardId') as string; const body = { - query: `query ($boardId: Int!) { + query: `query ($boardId: ID!) { boards ( ids: [$boardId]){ - groups () { + groups { id title } @@ -234,10 +237,10 @@ export class MondayCom implements INodeType { try { if (resource === 'board') { if (operation === 'archive') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const body: IGraphqlBody = { - query: `mutation ($id: Int!) { + query: `mutation ($id: ID!) { archive_board (board_id: $id) { id } @@ -256,7 +259,7 @@ export class MondayCom implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i); const body: IGraphqlBody = { - query: `mutation ($name: String!, $kind: BoardKind!, $templateId: Int) { + query: `mutation ($name: String!, $kind: BoardKind!, $templateId: ID) { create_board (board_name: $name, board_kind: $kind, template_id: $templateId) { id } @@ -275,10 +278,10 @@ export class MondayCom implements INodeType { responseData = responseData.data.create_board; } if (operation === 'get') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const body: IGraphqlBody = { - query: `query ($id: [Int]) { + query: `query ($id: [ID!]) { boards (ids: $id){ id name @@ -286,7 +289,7 @@ export class MondayCom implements INodeType { state board_folder_id board_kind - owner() { + owners { id } } @@ -311,7 +314,7 @@ export class MondayCom implements INodeType { state board_folder_id board_kind - owner() { + owners { id } } @@ -332,13 +335,13 @@ export class MondayCom implements INodeType { } if (resource === 'boardColumn') { if (operation === 'create') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const title = this.getNodeParameter('title', i) as string; const columnType = this.getNodeParameter('columnType', i) as string; const additionalFields = this.getNodeParameter('additionalFields', i); const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $title: String!, $columnType: ColumnType, $defaults: JSON ) { + query: `mutation ($boardId: ID!, $title: String!, $columnType: ColumnType!, $defaults: JSON ) { create_column (board_id: $boardId, title: $title, column_type: $columnType, defaults: $defaults) { id } @@ -367,12 +370,12 @@ export class MondayCom implements INodeType { responseData = responseData.data.create_column; } if (operation === 'getAll') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const body: IGraphqlBody = { - query: `query ($boardId: [Int]) { + query: `query ($boardId: [ID!]) { boards (ids: $boardId){ - columns() { + columns { id title type @@ -393,11 +396,11 @@ export class MondayCom implements INodeType { } if (resource === 'boardGroup') { if (operation === 'create') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const name = this.getNodeParameter('name', i) as string; const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $groupName: String!) { + query: `mutation ($boardId: ID!, $groupName: String!) { create_group (board_id: $boardId, group_name: $groupName) { id } @@ -412,11 +415,11 @@ export class MondayCom implements INodeType { responseData = responseData.data.create_group; } if (operation === 'delete') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const groupId = this.getNodeParameter('groupId', i) as string; const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $groupId: String!) { + query: `mutation ($boardId: ID!, $groupId: String!) { delete_group (board_id: $boardId, group_id: $groupId) { id } @@ -431,13 +434,13 @@ export class MondayCom implements INodeType { responseData = responseData.data.delete_group; } if (operation === 'getAll') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const body: IGraphqlBody = { - query: `query ($boardId: [Int]) { + query: `query ($boardId: [ID!]) { boards (ids: $boardId, ){ id - groups() { + groups { id title color @@ -457,11 +460,11 @@ export class MondayCom implements INodeType { } if (resource === 'boardItem') { if (operation === 'addUpdate') { - const itemId = parseInt(this.getNodeParameter('itemId', i) as string, 10); + const itemId = this.getNodeParameter('itemId', i); const value = this.getNodeParameter('value', i) as string; const body: IGraphqlBody = { - query: `mutation ($itemId: Int!, $value: String!) { + query: `mutation ($itemId: ID!, $value: String!) { create_update (item_id: $itemId, body: $value) { id } @@ -476,13 +479,13 @@ export class MondayCom implements INodeType { responseData = responseData.data.create_update; } if (operation === 'changeColumnValue') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); - const itemId = parseInt(this.getNodeParameter('itemId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); + const itemId = this.getNodeParameter('itemId', i); const columnId = this.getNodeParameter('columnId', i) as string; const value = this.getNodeParameter('value', i) as string; const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $itemId: Int!, $columnId: String!, $value: JSON!) { + query: `mutation ($boardId: ID!, $itemId: ID!, $columnId: String!, $value: JSON!) { change_column_value (board_id: $boardId, item_id: $itemId, column_id: $columnId, value: $value) { id } @@ -507,12 +510,12 @@ export class MondayCom implements INodeType { responseData = responseData.data.change_column_value; } if (operation === 'changeMultipleColumnValues') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); - const itemId = parseInt(this.getNodeParameter('itemId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); + const itemId = this.getNodeParameter('itemId', i); const columnValues = this.getNodeParameter('columnValues', i) as string; const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $itemId: Int!, $columnValues: JSON!) { + query: `mutation ($boardId: ID!, $itemId: ID!, $columnValues: JSON!) { change_multiple_column_values (board_id: $boardId, item_id: $itemId, column_values: $columnValues) { id } @@ -536,13 +539,13 @@ export class MondayCom implements INodeType { responseData = responseData.data.change_multiple_column_values; } if (operation === 'create') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const groupId = this.getNodeParameter('groupId', i) as string; const itemName = this.getNodeParameter('name', i) as string; const additionalFields = this.getNodeParameter('additionalFields', i); const body: IGraphqlBody = { - query: `mutation ($boardId: Int!, $groupId: String!, $itemName: String!, $columnValues: JSON) { + query: `mutation ($boardId: ID!, $groupId: String!, $itemName: String!, $columnValues: JSON) { create_item (board_id: $boardId, group_id: $groupId, item_name: $itemName, column_values: $columnValues) { id } @@ -571,10 +574,10 @@ export class MondayCom implements INodeType { responseData = responseData.data.create_item; } if (operation === 'delete') { - const itemId = parseInt(this.getNodeParameter('itemId', i) as string, 10); + const itemId = this.getNodeParameter('itemId', i); const body: IGraphqlBody = { - query: `mutation ($itemId: Int!) { + query: `mutation ($itemId: ID!) { delete_item (item_id: $itemId) { id } @@ -587,24 +590,27 @@ export class MondayCom implements INodeType { responseData = responseData.data.delete_item; } if (operation === 'get') { - const itemIds = (this.getNodeParameter('itemId', i) as string) - .split(',') - .map((n) => parseInt(n, 10)); + const itemIds = (this.getNodeParameter('itemId', i) as string).split(','); const body: IGraphqlBody = { - query: `query ($itemId: [Int!]){ + query: `query ($itemId: [ID!]){ items (ids: $itemId) { id name created_at state - column_values() { + column_values { id text - title type value - additional_info + column { + + title + archived + description + settings_str + } } } }`, @@ -616,101 +622,128 @@ export class MondayCom implements INodeType { responseData = responseData.data.items; } if (operation === 'getAll') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const groupId = this.getNodeParameter('groupId', i) as string; const returnAll = this.getNodeParameter('returnAll', i); - const body: IGraphqlBody = { - query: `query ($boardId: [Int], $groupId: [String], $page: Int, $limit: Int) { - boards (ids: $boardId) { - groups (ids: $groupId) { - id - items(limit: $limit, page: $page) { - id - name - created_at - state - column_values() { - id - text - title - type - value - additional_info - } - } + const fieldsToReturn = ` + { + id + name + created_at + state + column_values { + id + text + type + value + column { + title + archived + description + settings_str + } + } + } + `; + + const body = { + query: `query ($boardId: [ID!], $groupId: [String], $limit: Int) { + boards(ids: $boardId) { + groups(ids: $groupId) { + id + items_page(limit: $limit) { + cursor + items ${fieldsToReturn} } } - }`, + } + }`, variables: { boardId, groupId, + limit: 100, }, }; if (returnAll) { - responseData = await mondayComApiRequestAllItems.call( + responseData = await mondayComApiPaginatedRequest.call( this, - 'data.boards[0].groups[0].items', - body, + 'data.boards[0].groups[0].items_page', + fieldsToReturn, + body as IDataObject, ); } else { body.variables.limit = this.getNodeParameter('limit', i); responseData = await mondayComApiRequest.call(this, body); - responseData = responseData.data.boards[0].groups[0].items; + responseData = responseData.data.boards[0].groups[0].items_page.items; } } if (operation === 'getByColumnValue') { - const boardId = parseInt(this.getNodeParameter('boardId', i) as string, 10); + const boardId = this.getNodeParameter('boardId', i); const columnId = this.getNodeParameter('columnId', i) as string; const columnValue = this.getNodeParameter('columnValue', i) as string; const returnAll = this.getNodeParameter('returnAll', i); - const body: IGraphqlBody = { - query: `query ($boardId: Int!, $columnId: String!, $columnValue: String!, $page: Int, $limit: Int ){ - items_by_column_values (board_id: $boardId, column_id: $columnId, column_value: $columnValue, page: $page, limit: $limit) { - id - name - created_at - state - board { - id - } - column_values() { - id - text - title - type - value - additional_info - } - } - }`, + const fieldsToReturn = `{ + id + name + created_at + state + board { + id + } + column_values { + id + text + type + value + column { + title + archived + description + settings_str + } + } + }`; + const body = { + query: `query ($boardId: ID!, $columnId: String!, $columnValue: String!, $limit: Int) { + items_page_by_column_values( + limit: $limit + board_id: $boardId + columns: [{column_id: $columnId, column_values: [$columnValue]}] + ) { + cursor + items ${fieldsToReturn} + } + }`, variables: { boardId, columnId, columnValue, + limit: 100, }, }; if (returnAll) { - responseData = await mondayComApiRequestAllItems.call( + responseData = await mondayComApiPaginatedRequest.call( this, - 'data.items_by_column_values', - body, + 'data.items_page_by_column_values', + fieldsToReturn, + body as IDataObject, ); } else { body.variables.limit = this.getNodeParameter('limit', i); responseData = await mondayComApiRequest.call(this, body); - responseData = responseData.data.items_by_column_values; + responseData = responseData.data.items_page_by_column_values.items; } } if (operation === 'move') { const groupId = this.getNodeParameter('groupId', i) as string; - const itemId = parseInt(this.getNodeParameter('itemId', i) as string, 10); + const itemId = this.getNodeParameter('itemId', i); const body: IGraphqlBody = { - query: `mutation ($groupId: String!, $itemId: Int!) { + query: `mutation ($groupId: String!, $itemId: ID!) { move_item_to_group (group_id: $groupId, item_id: $itemId) { id }