From 277b6b73c37187f524474364c3b58adbc15486e0 Mon Sep 17 00:00:00 2001 From: Marcus <56945030+maspio@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:37:52 +0100 Subject: [PATCH] feat(Notion (Beta) Node): Use resource locator component for database and page parameters (#4340) * use resource locator component for database -> get (Notion V1/V2) * getDatabases search function for V1/V2 with url * updated database get list placeholder * get database RLC by url - regex support optional workspace domain names * fixed linting error * listSearch getDatabases support filter query * support extractValue in getCurrentNodeParameter for RLC * RLC for database page create/getAll operation * RLC for database get operation support "By ID" with optional v param. * use RLC in append blocks operation * use RLC in NotionTrigger.nodes.ts * removed unused loadOptions getDatabases * support database RLC in createPage/createDbPage operation * page create operation use RLC for parent page param * page archive operation use RLC for page param * removed unused imports * fixed missing extractPageId in NotionV1.node.ts * database page get operation use RLC for page param * database page update operation use RLC for page param * block getAll children operation use RLC for page param * block append operation use RLC for block param * support databaseId with optional '-' characters * support blockId with optional '-' characters * support pageId with optional '-' characters * improved RLC descriptions and hints * NotionTrigger node support databseId with optional '-' characters * stricter RLC by ID regex rules for uuids * stricter RLC by URL regex rules for uuids * stricter RLC by ID regex rules for uuids (support max length) * RLC regex from URL allow both http and https * RLC by ID only allow uuid v4 with optional dash * removed RLC from URL hint "Use Notion's copy link..." * RLC from URL only allow uuid v4 * DB Status Column: Support Simplify Properties * Notion Credentials: Support custom Notion-Version header Use latest Notion-Version 2022-02-22 if not set * DB Status Column: Support DB Page Create/Update * DB Status Column: Support DB Page GetMany Filters * removed unused paginationToken args * Database Get: RLC by URL improve validation error message --- packages/core/src/NodeExecuteFunctions.ts | 21 +- .../nodes/Notion/BlockDescription.ts | 104 ++++++- packages/nodes-base/nodes/Notion/Blocks.ts | 65 ++++- .../nodes/Notion/DatabaseDescription.ts | 63 ++++- .../nodes/Notion/DatabasePageDescription.ts | 266 ++++++++++++++++-- packages/nodes-base/nodes/Notion/Filters.ts | 16 ++ .../nodes/Notion/GenericFunctions.ts | 35 +++ .../nodes/Notion/NotionTrigger.node.ts | 96 ++++--- .../nodes/Notion/PageDescription.ts | 104 ++++++- .../nodes/Notion/SearchFunctions.ts | 38 +++ .../nodes/Notion/v1/NotionV1.node.ts | 77 ++--- .../nodes/Notion/v2/NotionV2.node.ts | 87 ++---- packages/workflow/src/Interfaces.ts | 5 +- 13 files changed, 775 insertions(+), 202 deletions(-) create mode 100644 packages/nodes-base/nodes/Notion/SearchFunctions.ts diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index 38af75fefe..581129c5a3 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -2674,6 +2674,7 @@ export function getLoadOptionsFunctions( }, getCurrentNodeParameter: ( parameterPath: string, + options?: IGetNodeParameterOptions, ): NodeParameterValueType | object | undefined => { const nodeParameters = additionalData.currentNodeParameters; @@ -2681,7 +2682,25 @@ export function getLoadOptionsFunctions( parameterPath = `${path.split('.').slice(1, -1).join('.')}.${parameterPath.slice(1)}`; } - return get(nodeParameters, parameterPath); + let returnData = get(nodeParameters, parameterPath); + + // This is outside the try/catch because it throws errors with proper messages + if (options?.extractValue) { + const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); + if (nodeType === undefined) { + throw new Error( + `Node type "${node.type}" is not known so can not return parameter value!`, + ); + } + returnData = extractValue( + returnData, + parameterPath, + node, + nodeType, + ) as NodeParameterValueType; + } + + return returnData; }, getCurrentNodeParameters: (): INodeParameters | undefined => { return additionalData.currentNodeParameters; diff --git a/packages/nodes-base/nodes/Notion/BlockDescription.ts b/packages/nodes-base/nodes/Notion/BlockDescription.ts index 932676b9c8..82d88d27fe 100644 --- a/packages/nodes-base/nodes/Notion/BlockDescription.ts +++ b/packages/nodes-base/nodes/Notion/BlockDescription.ts @@ -37,38 +37,122 @@ export const blockFields: INodeProperties[] = [ /* block:append */ /* -------------------------------------------------------------------------- */ { - displayName: 'Block ID or Link', + displayName: 'Block', name: 'blockId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Page-b4eeb113e118403ba450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Block URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Block ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['block'], operation: ['append'], }, }, - description: - "The Block URL from Notion's 'copy link' functionality (or just the ID contained within the URL). Pages are also blocks, so you can use a page URL/ID here too.", + description: "The Notion Block to append blocks to", }, ...blocks('block', 'append'), /* -------------------------------------------------------------------------- */ /* block:getAll */ /* -------------------------------------------------------------------------- */ { - displayName: 'Block ID or Link', + displayName: 'Block', name: 'blockId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Page-b4eeb113e118403ba450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Block URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Block ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['block'], operation: ['getAll'], }, }, - description: - "The Block URL from Notion's 'copy link' functionality (or just the ID contained within the URL). Pages are also blocks, so you can use a page URL/ID here too.", + description: "The Notion Block to get all children from", }, { displayName: 'Return All', diff --git a/packages/nodes-base/nodes/Notion/Blocks.ts b/packages/nodes-base/nodes/Notion/Blocks.ts index c1e9a872db..f83baf0a95 100644 --- a/packages/nodes-base/nodes/Notion/Blocks.ts +++ b/packages/nodes-base/nodes/Notion/Blocks.ts @@ -196,20 +196,69 @@ const typeMention: INodeProperties[] = [ description: 'The ID of the page being mentioned', }, { - displayName: 'Database Name or ID', + displayName: 'Database', name: 'database', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getDatabases', - }, + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + modes: [ + { + displayName: 'Database', + name: 'list', + type: 'list', + placeholder: 'Select a Database...', + typeOptions: { + searchListMethod: 'getDatabases', + searchable: true, + }, + }, + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/0fe2f7de558b471eab07e9d871cdf4a9?v=f2d424ba0c404733a3f500c78c881610', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { mentionType: ['database'], }, }, - default: '', - description: - 'The ID of the database being mentioned. Choose from the list, or specify an ID using an expression.', + description: "The Notion Database being mentioned", }, { displayName: 'Range', diff --git a/packages/nodes-base/nodes/Notion/DatabaseDescription.ts b/packages/nodes-base/nodes/Notion/DatabaseDescription.ts index 8d0be72bd5..8deb0a73d1 100644 --- a/packages/nodes-base/nodes/Notion/DatabaseDescription.ts +++ b/packages/nodes-base/nodes/Notion/DatabaseDescription.ts @@ -67,20 +67,73 @@ export const databaseFields: INodeProperties[] = [ /* -------------------------------------------------------------------------- */ /* database:get */ /* -------------------------------------------------------------------------- */ + { - displayName: 'Database Link or ID', + displayName: 'Database', name: 'databaseId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, required: true, + modes: [ + { + displayName: 'Database', + name: 'list', + type: 'list', + placeholder: 'Select a Database...', + typeOptions: { + searchListMethod: 'getDatabases', + searchable: true, + }, + }, + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/0fe2f7de558b471eab07e9d871cdf4a9?v=f2d424ba0c404733a3f500c78c881610', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database URL. Hint: use the URL of the database itself, not a page containing it.', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['database'], operation: ['get'], }, }, - description: - "The Database URL from Notion's 'copy link' functionality (or just the ID contained within the URL)", + description: "The Notion Database to get", }, /* -------------------------------------------------------------------------- */ /* database:getAll */ diff --git a/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts b/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts index 960f4c53f5..5629d5a5ed 100644 --- a/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts +++ b/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts @@ -86,22 +86,71 @@ export const databasePageFields: INodeProperties[] = [ /* databasePage:create */ /* -------------------------------------------------------------------------- */ { - displayName: 'Database Name or ID', + displayName: 'Database', name: 'databaseId', - type: 'options', - default: '', - typeOptions: { - loadOptionsMethod: 'getDatabases', - }, + type: 'resourceLocator', + default: { mode: 'list', value: '' }, required: true, + modes: [ + { + displayName: 'Database', + name: 'list', + type: 'list', + placeholder: 'Select a Database...', + typeOptions: { + searchListMethod: 'getDatabases', + searchable: true, + }, + }, + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/0fe2f7de558b471eab07e9d871cdf4a9?v=f2d424ba0c404733a3f500c78c881610', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['databasePage'], operation: ['create'], }, }, - description: - "The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL). Choose from the list, or specify an ID using an expression.", + description: "The Notion Database to operate on", }, { displayName: 'Title', @@ -252,6 +301,22 @@ export const databasePageFields: INodeProperties[] = [ description: 'Name of the option you want to set. Choose from the list, or specify an ID using an expression.', }, + { + displayName: 'Status Name or ID', + name: 'statusValue', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPropertySelectValues', + }, + displayOptions: { + show: { + type: ['status'], + }, + }, + default: '', + description: + 'Name of the option you want to set. Choose from the list, or specify an ID using an expression.', + }, { displayName: 'Email', name: 'emailValue', @@ -471,19 +536,61 @@ export const databasePageFields: INodeProperties[] = [ /* databasePage:update */ /* -------------------------------------------------------------------------- */ { - displayName: 'Database Page Link or ID', + displayName: 'Database Page', name: 'pageId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Database-Page-b4eeb113e118403ba450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database Page URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database Page ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['databasePage'], operation: ['update'], }, }, - description: - "The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)", + description: "The Notion Database Page to update", }, { displayName: 'Simplify', @@ -620,6 +727,22 @@ export const databasePageFields: INodeProperties[] = [ }, default: '', }, + { + displayName: 'Status Name or ID', + name: 'statusValue', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getDatabaseOptionsFromPage', + }, + displayOptions: { + show: { + type: ['status'], + }, + }, + default: '', + description: + 'Name of the option you want to set. Choose from the list, or specify an ID using an expression.', + }, { displayName: 'Email', name: 'emailValue', @@ -837,11 +960,54 @@ export const databasePageFields: INodeProperties[] = [ /* databasePage:get */ /* -------------------------------------------------------------------------- */ { - displayName: 'Database Page Link or ID', + displayName: 'Database Page', name: 'pageId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Database-Page-b4eeb113e118403ba450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database Page URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database Page ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { version: [2], @@ -849,8 +1015,7 @@ export const databasePageFields: INodeProperties[] = [ operation: ['get'], }, }, - description: - "The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)", + description: "The Notion Database Page to get", }, { displayName: 'Simplify', @@ -870,22 +1035,71 @@ export const databasePageFields: INodeProperties[] = [ /* databasePage:getAll */ /* -------------------------------------------------------------------------- */ { - displayName: 'Database Name or ID', + displayName: 'Database', name: 'databaseId', - type: 'options', - description: - 'Choose from the list, or specify an ID using an expression', - typeOptions: { - loadOptionsMethod: 'getDatabases', - }, - default: '', + type: 'resourceLocator', + default: { mode: 'list', value: '' }, required: true, + modes: [ + { + displayName: 'Database', + name: 'list', + type: 'list', + placeholder: 'Select a Database...', + typeOptions: { + searchListMethod: 'getDatabases', + searchable: true, + }, + }, + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/0fe2f7de558b471eab07e9d871cdf4a9?v=f2d424ba0c404733a3f500c78c881610', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['databasePage'], operation: ['getAll'], }, }, + description: "The Notion Database to operate on", }, { displayName: 'Return All', diff --git a/packages/nodes-base/nodes/Notion/Filters.ts b/packages/nodes-base/nodes/Notion/Filters.ts index bfde770052..11fb386543 100644 --- a/packages/nodes-base/nodes/Notion/Filters.ts +++ b/packages/nodes-base/nodes/Notion/Filters.ts @@ -100,6 +100,22 @@ export const filters = (conditions: any) => [ }, default: '', }, + { + displayName: 'Status Name or ID', + name: 'statusValue', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getPropertySelectValues', + }, + displayOptions: { + show: { + type: ['status'], + }, + }, + default: '', + description: + 'Choose from the list, or specify an ID using an expression', + }, { displayName: 'Email', name: 'emailValue', diff --git a/packages/nodes-base/nodes/Notion/GenericFunctions.ts b/packages/nodes-base/nodes/Notion/GenericFunctions.ts index 9d39594bb9..bade4279ca 100644 --- a/packages/nodes-base/nodes/Notion/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Notion/GenericFunctions.ts @@ -337,6 +337,12 @@ function getPropertyKeyValue(value: any, type: string, timezone: string, version select: version === 1 ? { id: value.selectValue } : { name: value.selectValue }, }; break; + case 'status': + result = { + type: 'status', + status: { name: value.statusValue }, + }; + break; case 'date': const format = getDateFormat(value.includeTime); const timezoneValue = value.timezone === 'default' ? timezone : value.timezone; @@ -542,6 +548,8 @@ function simplifyProperty(property: any) { // tslint:disable-next-line: no-any (file: { type: string; [key: string]: any }) => file[file.type].url, ); + } else if (['status'].includes(property.type)) { + result = property[type].name; } return result; } @@ -622,6 +630,7 @@ export function getConditions() { checkbox: 'checkbox', select: 'select', multi_select: 'multi_select', + status: 'status', date: 'date', people: 'people', files: 'files', @@ -660,6 +669,7 @@ export function getConditions() { checkbox: ['equals', 'does_not_equal'], select: ['equals', 'does_not_equal', 'is_empty', 'is_not_empty'], multi_select: ['contains', 'does_not_equal', 'is_empty', 'is_not_empty'], + status: ['equals', 'does_not_equal'], date: [ 'equals', 'before', @@ -934,3 +944,28 @@ export function validateJSON(json: string | undefined): any { } return result; } + +/** + * Manually extract a richtext's database mention RLC parameter. + * @param blockValues the blockUi.blockValues node parameter. + */ +export function extractDatabaseMentionRLC(blockValues: IDataObject[]) { + blockValues.forEach(bv => { + if (bv.richText && bv.text) { + const texts = (bv.text as { text: [{ textType: string, mentionType: string, database: string | { value: string, mode: string, __rl: boolean, __regex: string } }] }).text; + texts.forEach(txt => { + if (txt.textType === 'mention' && txt.mentionType === 'database') { + if (typeof txt.database === 'object' && txt.database.__rl) { + if (txt.database.__regex) { + const regex = new RegExp(txt.database.__regex); + const extracted = regex.exec(txt.database.value); + txt.database = extracted![1]; + } else { + txt.database = txt.database.value; + } + } + } + }); + } + }); +} diff --git a/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts b/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts index 779b0d67b0..fd2528cc21 100644 --- a/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts +++ b/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts @@ -12,6 +12,7 @@ import { import { notionApiRequest, simplifyObjects } from './GenericFunctions'; import moment from 'moment'; +import { getDatabases } from './SearchFunctions'; export class NotionTrigger implements INodeType { description: INodeTypeDescription = { @@ -61,21 +62,70 @@ export class NotionTrigger implements INodeType { default: '', }, { - displayName: 'Database Name or ID', + displayName: 'Database', name: 'databaseId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getDatabases', - }, + type: 'resourceLocator', + default: { mode: 'list', value: '' }, + required: true, + modes: [ + { + displayName: 'Database', + name: 'list', + type: 'list', + placeholder: 'Select a Database...', + typeOptions: { + searchListMethod: 'getDatabases', + searchable: true, + }, + }, + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/0fe2f7de558b471eab07e9d871cdf4a9?v=f2d424ba0c404733a3f500c78c881610', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Database URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Database ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { event: ['pageAddedToDatabase', 'pagedUpdatedInDatabase'], }, }, - default: '', - required: true, - description: - 'The ID of this database. Choose from the list, or specify an ID using an expression.', + description: "The Notion Database to operate on", }, { displayName: 'Simplify', @@ -94,36 +144,14 @@ export class NotionTrigger implements INodeType { }; methods = { - loadOptions: { - async getDatabases(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const { results: databases } = await notionApiRequest.call(this, 'POST', `/search`, { - page_size: 100, - filter: { property: 'object', value: 'database' }, - }); - for (const database of databases) { - returnData.push({ - name: database.title[0]?.plain_text || database.id, - value: database.id, - }); - } - returnData.sort((a, b) => { - if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { - return 1; - } - return 0; - }); - return returnData; - }, + listSearch: { + getDatabases, }, }; async poll(this: IPollFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); - const databaseId = this.getNodeParameter('databaseId') as string; + const databaseId = this.getNodeParameter('databaseId', '', { extractValue: true }) as string; const event = this.getNodeParameter('event') as string; const simple = this.getNodeParameter('simple') as boolean; diff --git a/packages/nodes-base/nodes/Notion/PageDescription.ts b/packages/nodes-base/nodes/Notion/PageDescription.ts index d9d5ea562f..f44a8375c8 100644 --- a/packages/nodes-base/nodes/Notion/PageDescription.ts +++ b/packages/nodes-base/nodes/Notion/PageDescription.ts @@ -76,11 +76,54 @@ export const pageFields: INodeProperties[] = [ /* page:archive */ /* -------------------------------------------------------------------------- */ { - displayName: 'Page Link or ID', + displayName: 'Page', name: 'pageId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Page-b4eeb113e118403aa450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Page URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Page ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { version: [2], @@ -88,8 +131,7 @@ export const pageFields: INodeProperties[] = [ operation: ['archive'], }, }, - description: - "The Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)", + description: "The Notion Page to archive", }, { displayName: 'Simplify', @@ -109,19 +151,61 @@ export const pageFields: INodeProperties[] = [ /* page:create */ /* -------------------------------------------------------------------------- */ { - displayName: 'Parent Page ID or Link', + displayName: 'Parent Page', name: 'pageId', - type: 'string', - default: '', + type: 'resourceLocator', + default: { mode: 'url', value: '' }, required: true, + modes: [ + { + displayName: 'Link', + name: 'url', + type: 'string', + placeholder: + 'https://www.notion.so/My-Page-b4eeb113e118403aa450af65ac25f0b9', + validation: [ + { + type: 'regex', + properties: { + regex: + '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}).*', + errorMessage: 'Not a valid Notion Page URL', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '(?:https|http):\/\/www.notion.so\/(?:[a-z0-9\-]{2,}\/)?(?:[a-zA-Z0-9\-]{2,}-)?([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})', + }, + }, + { + displayName: 'ID', + name: 'id', + type: 'string', + placeholder: 'ab1545b247fb49fa92d6f4b49f4d8116', + validation: [ + { + type: 'regex', + properties: { + regex: '^(([0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12})|([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}))[ \t]*', + errorMessage: 'Not a valid Notion Page ID', + }, + }, + ], + extractValue: { + type: 'regex', + regex: '^([0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12})', + }, + url: '=https://www.notion.so/{{$value.replace(/-/g, "")}}', + }, + ], displayOptions: { show: { resource: ['page'], operation: ['create'], }, }, - description: - "The URL from Notion's 'copy link' functionality (or just the ID contained within the URL)", + description: "The Notion Database Page to create a child page for", }, { displayName: 'Title', diff --git a/packages/nodes-base/nodes/Notion/SearchFunctions.ts b/packages/nodes-base/nodes/Notion/SearchFunctions.ts new file mode 100644 index 0000000000..dce1a901a7 --- /dev/null +++ b/packages/nodes-base/nodes/Notion/SearchFunctions.ts @@ -0,0 +1,38 @@ +import { IDataObject, ILoadOptionsFunctions, INodeListSearchItems, INodeListSearchResult } from "n8n-workflow"; +import { notionApiRequestAllItems } from "./GenericFunctions"; + +export async function getDatabases( + this: ILoadOptionsFunctions, + filter?: string, +): Promise { + const returnData: INodeListSearchItems[] = []; + const body: IDataObject = { + page_size: 100, + query: filter, + filter: { property: 'object', value: 'database' }, + }; + const databases = await notionApiRequestAllItems.call( + this, + 'results', + 'POST', + `/search`, + body, + ); + for (const database of databases) { + returnData.push({ + name: database.title[0]?.plain_text || database.id, + value: database.id, + url: database.url, + }); + } + returnData.sort((a, b) => { + if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { + return -1; + } + if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { + return 1; + } + return 0; + }); + return { results: returnData }; +} diff --git a/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts b/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts index c9cfc42ff0..29ed5cd68a 100644 --- a/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts +++ b/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts @@ -12,6 +12,7 @@ import { import { extractDatabaseId, + extractDatabaseMentionRLC, extractPageId, formatBlocks, formatTitle, @@ -27,6 +28,7 @@ import { import moment from 'moment-timezone'; import { versionDescription } from './VersionDescription'; +import { getDatabases } from '../SearchFunctions'; export class NotionV1 implements INodeType { description: INodeTypeDescription; @@ -39,40 +41,13 @@ export class NotionV1 implements INodeType { } methods = { + listSearch: { + getDatabases, + }, loadOptions: { - async getDatabases(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const body: IDataObject = { - page_size: 100, - filter: { property: 'object', value: 'database' }, - }; - const databases = await notionApiRequestAllItems.call( - this, - 'results', - 'POST', - `/search`, - body, - ); - for (const database of databases) { - returnData.push({ - name: database.title[0]?.plain_text || database.id, - value: database.id, - }); - } - returnData.sort((a, b) => { - if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { - return 1; - } - return 0; - }); - return returnData; - }, async getDatabaseProperties(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); for (const key of Object.keys(properties)) { //remove parameters that cannot be set from the API. @@ -106,7 +81,7 @@ export class NotionV1 implements INodeType { }, async getFilterProperties(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); for (const key of Object.keys(properties)) { returnData.push({ @@ -130,7 +105,7 @@ export class NotionV1 implements INodeType { }, async getPropertySelectValues(this: ILoadOptionsFunctions): Promise { const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|'); - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const resource = this.getCurrentNodeParameter('resource') as string; const operation = this.getCurrentNodeParameter('operation') as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); @@ -167,7 +142,7 @@ export class NotionV1 implements INodeType { }, async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const pageId = this.getCurrentNodeParameter('pageId') as string; + const pageId = extractPageId(this.getCurrentNodeParameter('pageId', { extractValue: true }) as string); const { parent: { database_id: databaseId }, } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`); @@ -205,7 +180,7 @@ export class NotionV1 implements INodeType { async getDatabaseOptionsFromPage( this: ILoadOptionsFunctions, ): Promise { - const pageId = this.getCurrentNodeParameter('pageId') as string; + const pageId = extractPageId(this.getCurrentNodeParameter('pageId', { extractValue: true }) as string); const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|'); const { parent: { database_id: databaseId }, @@ -253,11 +228,11 @@ export class NotionV1 implements INodeType { if (resource === 'block') { if (operation === 'append') { for (let i = 0; i < length; i++) { - const blockId = extractPageId(this.getNodeParameter('blockId', i) as string); + const blockId = extractPageId(this.getNodeParameter('blockId', i, '', { extractValue: true }) as string); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); const body: IDataObject = { - children: formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ), + children: formatBlocks(blockValues), }; const block = await notionApiRequest.call( this, @@ -276,7 +251,7 @@ export class NotionV1 implements INodeType { if (operation === 'getAll') { for (let i = 0; i < length; i++) { - const blockId = extractPageId(this.getNodeParameter('blockId', i) as string); + const blockId = extractPageId(this.getNodeParameter('blockId', i, '', { extractValue: true }) as string); const returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll) { responseData = await notionApiRequestAllItems.call( @@ -310,7 +285,7 @@ export class NotionV1 implements INodeType { if (resource === 'database') { if (operation === 'get') { for (let i = 0; i < length; i++) { - const databaseId = extractDatabaseId(this.getNodeParameter('databaseId', i) as string); + const databaseId = extractDatabaseId(this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string); responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); const executionData = this.helpers.constructExecutionMetaData( @@ -359,7 +334,7 @@ export class NotionV1 implements INodeType { parent: {}, properties: {}, }; - body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string; + body.parent['database_id'] = this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string; const properties = this.getNodeParameter( 'propertiesUi.propertyValues', i, @@ -368,9 +343,9 @@ export class NotionV1 implements INodeType { if (properties.length !== 0) { body.properties = mapProperties(properties, timezone) as IDataObject; } - body.children = formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); + body.children = formatBlocks(blockValues); responseData = await notionApiRequest.call(this, 'POST', '/pages', body); if (simple === true) { responseData = simplifyObjects(responseData, false, 1); @@ -387,7 +362,7 @@ export class NotionV1 implements INodeType { if (operation === 'getAll') { for (let i = 0; i < length; i++) { const simple = this.getNodeParameter('simple', 0) as boolean; - const databaseId = this.getNodeParameter('databaseId', i) as string; + const databaseId = this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string; const returnAll = this.getNodeParameter('returnAll', i) as boolean; const filters = this.getNodeParameter('options.filter', i, {}) as IDataObject; const sort = this.getNodeParameter('options.sort.sortValue', i, []) as IDataObject[]; @@ -451,7 +426,7 @@ export class NotionV1 implements INodeType { if (operation === 'update') { for (let i = 0; i < length; i++) { - const pageId = extractPageId(this.getNodeParameter('pageId', i) as string); + const pageId = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); const simple = this.getNodeParameter('simple', i) as boolean; const properties = this.getNodeParameter( 'propertiesUi.propertyValues', @@ -521,11 +496,11 @@ export class NotionV1 implements INodeType { parent: {}, properties: {}, }; - body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i) as string); + body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); body.properties = formatTitle(this.getNodeParameter('title', i) as string); - body.children = formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); + body.children = formatBlocks(blockValues); responseData = await notionApiRequest.call(this, 'POST', '/pages', body); if (simple === true) { responseData = simplifyObjects(responseData, false, 1); diff --git a/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts index ed44cd01dc..55be1b8837 100644 --- a/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts +++ b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts @@ -15,6 +15,7 @@ import { import { downloadFiles, extractDatabaseId, + extractDatabaseMentionRLC, extractPageId, formatBlocks, formatTitle, @@ -31,6 +32,7 @@ import { import moment from 'moment-timezone'; import { versionDescription } from './VersionDescription'; +import { getDatabases } from '../SearchFunctions'; export class NotionV2 implements INodeType { description: INodeTypeDescription; @@ -43,40 +45,13 @@ export class NotionV2 implements INodeType { } methods = { + listSearch: { + getDatabases, + }, loadOptions: { - async getDatabases(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const body: IDataObject = { - page_size: 100, - filter: { property: 'object', value: 'database' }, - }; - const databases = await notionApiRequestAllItems.call( - this, - 'results', - 'POST', - `/search`, - body, - ); - for (const database of databases) { - returnData.push({ - name: database.title[0]?.plain_text || database.id, - value: database.id, - }); - } - returnData.sort((a, b) => { - if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { - return 1; - } - return 0; - }); - return returnData; - }, async getDatabaseProperties(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); for (const key of Object.keys(properties)) { //remove parameters that cannot be set from the API. @@ -109,7 +84,7 @@ export class NotionV2 implements INodeType { }, async getFilterProperties(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); for (const key of Object.keys(properties)) { returnData.push({ @@ -133,18 +108,18 @@ export class NotionV2 implements INodeType { }, async getPropertySelectValues(this: ILoadOptionsFunctions): Promise { const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|'); - const databaseId = this.getCurrentNodeParameter('databaseId') as string; + const databaseId = this.getCurrentNodeParameter('databaseId', { extractValue: true }) as string; const resource = this.getCurrentNodeParameter('resource') as string; const operation = this.getCurrentNodeParameter('operation') as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); if (resource === 'databasePage') { - if (['multi_select', 'select'].includes(type) && operation === 'getAll') { + if (['multi_select', 'select', 'status'].includes(type) && operation === 'getAll') { return properties[name][type].options.map((option: IDataObject) => ({ name: option.name, value: option.name, })); } else if ( - ['multi_select', 'select'].includes(type) && + ['multi_select', 'select', 'status'].includes(type) && ['create', 'update'].includes(operation) ) { return properties[name][type].options.map((option: IDataObject) => ({ @@ -173,7 +148,7 @@ export class NotionV2 implements INodeType { }, async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise { const returnData: INodePropertyOptions[] = []; - const pageId = extractPageId(this.getCurrentNodeParameter('pageId') as string); + const pageId = extractPageId(this.getCurrentNodeParameter('pageId', { extractValue: true }) as string); const { parent: { database_id: databaseId }, } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`); @@ -211,7 +186,7 @@ export class NotionV2 implements INodeType { async getDatabaseOptionsFromPage( this: ILoadOptionsFunctions, ): Promise { - const pageId = extractPageId(this.getCurrentNodeParameter('pageId') as string); + const pageId = extractPageId(this.getCurrentNodeParameter('pageId', { extractValue: true }) as string); const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|'); const { parent: { database_id: databaseId }, @@ -260,11 +235,11 @@ export class NotionV2 implements INodeType { if (resource === 'block') { if (operation === 'append') { for (let i = 0; i < length; i++) { - const blockId = extractPageId(this.getNodeParameter('blockId', i) as string); + const blockId = extractPageId(this.getNodeParameter('blockId', i, '', { extractValue: true }) as string); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); const body: IDataObject = { - children: formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ), + children: formatBlocks(blockValues), }; const block = await notionApiRequest.call( this, @@ -283,7 +258,7 @@ export class NotionV2 implements INodeType { if (operation === 'getAll') { for (let i = 0; i < length; i++) { - const blockId = extractPageId(this.getNodeParameter('blockId', i) as string); + const blockId = extractPageId(this.getNodeParameter('blockId', i, '', { extractValue: true }) as string); const returnAll = this.getNodeParameter('returnAll', i) as boolean; if (returnAll) { @@ -325,7 +300,7 @@ export class NotionV2 implements INodeType { if (operation === 'get') { const simple = this.getNodeParameter('simple', 0) as boolean; for (let i = 0; i < length; i++) { - const databaseId = extractDatabaseId(this.getNodeParameter('databaseId', i) as string); + const databaseId = extractDatabaseId(this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string); responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); if (simple === true) { responseData = simplifyObjects(responseData, download)[0]; @@ -426,7 +401,7 @@ export class NotionV2 implements INodeType { if (resource === 'databasePage') { if (operation === 'create') { - const databaseId = this.getNodeParameter('databaseId', 0) as string; + const databaseId = this.getNodeParameter('databaseId', 0, '', { extractValue: true }) as string; const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`); let titleKey = ''; for (const key of Object.keys(properties)) { @@ -453,7 +428,7 @@ export class NotionV2 implements INodeType { ], }; } - body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string; + body.parent['database_id'] = this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string; const properties = this.getNodeParameter( 'propertiesUi.propertyValues', i, @@ -465,9 +440,9 @@ export class NotionV2 implements INodeType { mapProperties(properties, timezone, 2) as IDataObject, ); } - body.children = formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); + body.children = formatBlocks(blockValues); responseData = await notionApiRequest.call(this, 'POST', '/pages', body); if (simple === true) { responseData = simplifyObjects(responseData); @@ -483,7 +458,7 @@ export class NotionV2 implements INodeType { if (operation === 'get') { for (let i = 0; i < length; i++) { - const pageId = extractPageId(this.getNodeParameter('pageId', i) as string); + const pageId = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); const simple = this.getNodeParameter('simple', i) as boolean; responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`); if (simple === true) { @@ -502,7 +477,7 @@ export class NotionV2 implements INodeType { for (let i = 0; i < length; i++) { download = this.getNodeParameter('options.downloadFiles', 0, false) as boolean; const simple = this.getNodeParameter('simple', 0) as boolean; - const databaseId = this.getNodeParameter('databaseId', i) as string; + const databaseId = this.getNodeParameter('databaseId', i, '', { extractValue: true }) as string; const returnAll = this.getNodeParameter('returnAll', i) as boolean; const filterType = this.getNodeParameter('filterType', 0) as string; const conditions = this.getNodeParameter('filters.conditions', i, []) as IDataObject[]; @@ -577,7 +552,7 @@ export class NotionV2 implements INodeType { if (operation === 'update') { for (let i = 0; i < length; i++) { - const pageId = extractPageId(this.getNodeParameter('pageId', i) as string); + const pageId = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); const simple = this.getNodeParameter('simple', i) as boolean; const properties = this.getNodeParameter( 'propertiesUi.propertyValues', @@ -641,7 +616,7 @@ export class NotionV2 implements INodeType { if (resource === 'page') { if (operation === 'archive') { for (let i = 0; i < length; i++) { - const pageId = extractPageId(this.getNodeParameter('pageId', i) as string); + const pageId = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); const simple = this.getNodeParameter('simple', i) as boolean; responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, { archived: true, @@ -666,11 +641,11 @@ export class NotionV2 implements INodeType { parent: {}, properties: {}, }; - body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i) as string); + body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i, '', { extractValue: true }) as string); body.properties = formatTitle(this.getNodeParameter('title', i) as string); - body.children = formatBlocks( - this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[], - ); + const blockValues = this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]; + extractDatabaseMentionRLC(blockValues); + body.children = formatBlocks(blockValues); responseData = await notionApiRequest.call(this, 'POST', '/pages', body); if (simple === true) { responseData = simplifyObjects(responseData, download); diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index b93d34bbf8..d2637f5053 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -663,7 +663,10 @@ export interface ILoadOptionsFunctions { fallbackValue?: any, options?: IGetNodeParameterOptions, ): NodeParameterValueType | object; - getCurrentNodeParameter(parameterName: string): NodeParameterValueType | object | undefined; + getCurrentNodeParameter( + parameterName: string, + options?: IGetNodeParameterOptions, + ): NodeParameterValueType | object | undefined; getCurrentNodeParameters(): INodeParameters | undefined; getTimezone(): string; getRestApiUrl(): string;