diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts
index 549055fee6ed9..0861eab3b06d2 100644
--- a/packages/cli/src/Server.ts
+++ b/packages/cli/src/Server.ts
@@ -1436,7 +1436,7 @@ class App {
const testFunctionSearch =
credential.name === credentialType && !!credential.testedBy;
if (testFunctionSearch) {
- foundTestFunction = (node as unknown as INodeType).methods!.credentialTest![
+ foundTestFunction = (nodeType as unknown as INodeType).methods!.credentialTest![
credential.testedBy!
];
}
diff --git a/packages/nodes-base/nodes/Notion/BlockDescription.ts b/packages/nodes-base/nodes/Notion/BlockDescription.ts
index 75c04ad3ec9c9..5d1c15ec52b87 100644
--- a/packages/nodes-base/nodes/Notion/BlockDescription.ts
+++ b/packages/nodes-base/nodes/Notion/BlockDescription.ts
@@ -6,7 +6,7 @@ import {
blocks,
} from './Blocks';
-export const blockOperations: INodeProperties[] = [
+export const blockOperations = [
{
displayName: 'Operation',
name: 'operation',
@@ -20,28 +20,27 @@ export const blockOperations: INodeProperties[] = [
},
options: [
{
- name: 'Append',
+ name: 'Append After',
value: 'append',
description: 'Append a block',
},
{
- name: 'Get All',
+ name: 'Get Child Blocks',
value: 'getAll',
description: 'Get all children blocks',
},
],
default: 'append',
- description: 'The operation to perform.',
},
-];
+] as INodeProperties[];
-export const blockFields: INodeProperties[] = [
+export const blockFields = [
/* -------------------------------------------------------------------------- */
/* block:append */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Block ID',
+ displayName: 'Block ID or Link',
name: 'blockId',
type: 'string',
default: '',
@@ -56,14 +55,14 @@ export const blockFields: INodeProperties[] = [
],
},
},
- description: `The ID of block. A page it is also considered a block. Hence, a Page ID can be used as well.`,
+ 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`,
},
...blocks('block', 'append'),
/* -------------------------------------------------------------------------- */
/* block:getAll */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Block ID',
+ displayName: 'Block ID or Link',
name: 'blockId',
type: 'string',
default: '',
@@ -78,6 +77,7 @@ export const blockFields: INodeProperties[] = [
],
},
},
+ 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`,
},
{
displayName: 'Return All',
@@ -94,7 +94,7 @@ export const blockFields: INodeProperties[] = [
},
},
default: false,
- description: 'If all results should be returned or only up to a given limit.',
+ description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
@@ -118,6 +118,6 @@ export const blockFields: INodeProperties[] = [
maxValue: 100,
},
default: 50,
- description: 'How many results to return.',
+ description: 'How many results to return',
},
-];
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Notion/Blocks.ts b/packages/nodes-base/nodes/Notion/Blocks.ts
index 759a4a62613ee..e9f365485860e 100644
--- a/packages/nodes-base/nodes/Notion/Blocks.ts
+++ b/packages/nodes-base/nodes/Notion/Blocks.ts
@@ -95,35 +95,35 @@ const annotation: INodeProperties[] = [
name: 'bold',
type: 'boolean',
default: false,
- description: 'Whether the text is bolded.',
+ description: 'Whether the text is bolded',
},
{
displayName: 'Italic',
name: 'italic',
type: 'boolean',
default: false,
- description: 'Whether the text is italicized.',
+ description: 'Whether the text is italicized',
},
{
displayName: 'Strikethrough',
name: 'strikethrough',
type: 'boolean',
default: false,
- description: 'Whether the text is struck through.',
+ description: 'Whether the text is struck through',
},
{
displayName: 'Underline',
name: 'underline',
type: 'boolean',
default: false,
- description: 'Whether the text is underlined.',
+ description: 'Whether the text is underlined',
},
{
displayName: 'Code',
name: 'code',
type: 'boolean',
default: false,
- description: 'Whether the text is code style.',
+ description: 'Whether the text is code style',
},
{
displayName: 'Color',
@@ -131,10 +131,10 @@ const annotation: INodeProperties[] = [
type: 'options',
options: colors,
default: '',
- description: 'Color of the text.',
+ description: 'Color of the text',
},
],
- description: 'All annotations that apply to this rich text.',
+ description: 'All annotations that apply to this rich text',
},
];
@@ -169,7 +169,7 @@ const typeMention: INodeProperties[] = [
},
],
default: '',
- description: `An inline mention of a user, page, database, or date. In the app these are created by typing @ followed by the name of a user, page, database, or a date.`,
+ description: `An inline mention of a user, page, database, or date. In the app these are created by typing @ followed by the name of a user, page, database, or a date`,
},
{
displayName: 'User ID',
@@ -186,7 +186,7 @@ const typeMention: INodeProperties[] = [
},
},
default: '',
- description: 'The id of the user being mentioned.',
+ description: 'The ID of the user being mentioned',
},
{
displayName: 'Page ID',
@@ -200,7 +200,7 @@ const typeMention: INodeProperties[] = [
},
},
default: '',
- description: 'The id of the page being mentioned.',
+ description: 'The ID of the page being mentioned',
},
{
displayName: 'Database ID',
@@ -217,7 +217,7 @@ const typeMention: INodeProperties[] = [
},
},
default: '',
- description: 'The id of the database being mentioned.',
+ description: 'The ID of the database being mentioned',
},
{
displayName: 'Range',
@@ -231,7 +231,7 @@ const typeMention: INodeProperties[] = [
},
type: 'boolean',
default: false,
- description: 'Weather or not you want to define a date range.',
+ description: 'Weather or not you want to define a date range',
},
{
displayName: 'Date',
@@ -248,7 +248,7 @@ const typeMention: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date Start',
@@ -265,7 +265,7 @@ const typeMention: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date End',
@@ -282,7 +282,7 @@ const typeMention: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: `An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
+ description: `An ISO 8601 formatted date, with optional time. Represents the end of a date range`,
},
];
@@ -316,7 +316,8 @@ const typeText: INodeProperties[] = [
},
type: 'string',
default: '',
- description: `Text content. This field contains the actual content of your text and is probably the field you'll use most often.`,
+ description: `Text content. This field contains the actual content
+ of your text and is probably the field you'll use most often`,
},
{
displayName: 'Is Link',
@@ -346,7 +347,7 @@ const typeText: INodeProperties[] = [
},
type: 'string',
default: '',
- description: 'The URL that this link points to.',
+ description: 'The URL that this link points to',
},
];
@@ -395,8 +396,8 @@ export const text = (displayOptions: IDisplayOptions): INodeProperties[] => [
],
},
],
- description: 'Rich text in the block.',
- }];
+ description: 'Rich text in the block',
+ }] as INodeProperties[];
const todo = (type: string): INodeProperties[] => [{
@@ -411,8 +412,8 @@ const todo = (type: string): INodeProperties[] => [{
],
},
},
- description: 'Whether the to_do is checked or not.',
-}];
+ description: 'Whether the to_do is checked or not',
+}] as INodeProperties[];
const title = (type: string): INodeProperties[] => [{
displayName: 'Title',
@@ -426,8 +427,8 @@ const title = (type: string): INodeProperties[] => [{
],
},
},
- description: 'Plain text of page title.',
-}];
+ description: 'Plain text of page title',
+}] as INodeProperties[];
const richText = (displayOptions: IDisplayOptions): INodeProperties[] => [
{
@@ -449,7 +450,7 @@ const textContent = (displayOptions: IDisplayOptions): INodeProperties[] => [
},
];
-const block = (blockType: string) => {
+const block = (blockType: string): INodeProperties[] => {
const data: INodeProperties[] = [];
switch (blockType) {
case 'to_do':
@@ -549,7 +550,6 @@ export const blocks = (resource: string, operation: string): INodeProperties[] =
typeOptions: {
loadOptionsMethod: 'getBlockTypes',
},
- description: 'Type of block',
default: 'paragraph',
},
...block('paragraph'),
@@ -566,4 +566,3 @@ export const blocks = (resource: string, operation: string): INodeProperties[] =
],
},
];
-
diff --git a/packages/nodes-base/nodes/Notion/DatabaseDescription.ts b/packages/nodes-base/nodes/Notion/DatabaseDescription.ts
index 58dd1b94c88cd..10c6fdb16b619 100644
--- a/packages/nodes-base/nodes/Notion/DatabaseDescription.ts
+++ b/packages/nodes-base/nodes/Notion/DatabaseDescription.ts
@@ -2,13 +2,16 @@ import {
INodeProperties,
} from 'n8n-workflow';
-export const databaseOperations: INodeProperties[] = [
+export const databaseOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
+ version: [
+ 2,
+ ],
resource: [
'database',
],
@@ -25,19 +28,51 @@ export const databaseOperations: INodeProperties[] = [
value: 'getAll',
description: 'Get all databases',
},
+ {
+ name: 'Search',
+ value: 'search',
+ description: 'search databases using text search',
+ },
],
default: 'get',
- description: 'The operation to perform.',
},
-];
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ version: [
+ 1,
+ ],
+ resource: [
+ 'database',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a database',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all databases',
+ },
+ ],
+ default: 'get',
+ },
+] as INodeProperties[];
-export const databaseFields: INodeProperties[] = [
+export const databaseFields = [
/* -------------------------------------------------------------------------- */
/* database:get */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Database ID',
+ displayName: 'Database Link or ID',
name: 'databaseId',
type: 'string',
default: '',
@@ -52,6 +87,7 @@ export const databaseFields: INodeProperties[] = [
],
},
},
+ description: `The Database URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
},
/* -------------------------------------------------------------------------- */
/* database:getAll */
@@ -71,7 +107,7 @@ export const databaseFields: INodeProperties[] = [
},
},
default: false,
- description: 'If all results should be returned or only up to a given limit.',
+ description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
@@ -95,6 +131,172 @@ export const databaseFields: INodeProperties[] = [
maxValue: 100,
},
default: 50,
- description: 'How many results to return.',
+ description: 'How many results to return',
+ },
+ {
+ displayName: 'Simplify Output',
+ name: 'simple',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'getAll',
+ 'get',
+ ],
+ },
+ },
+ default: true,
+ description: 'Whether to return a simplified version of the response instead of the raw data',
+ },
+ /* -------------------------------------------------------------------------- */
+ /* database:search */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Search Text',
+ name: 'text',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'search',
+ ],
+ },
+ },
+ description: 'The text to search for',
+ },
+ {
+ displayName: 'Return All',
+ name: 'returnAll',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'search',
+ ],
+ },
+ },
+ default: false,
+ description: 'Whether to return all results or only up to a given limit',
+ },
+ {
+ displayName: 'Limit',
+ name: 'limit',
+ type: 'number',
+ displayOptions: {
+ show: {
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'search',
+ ],
+ returnAll: [
+ false,
+ ],
+ },
+ },
+ typeOptions: {
+ minValue: 1,
+ maxValue: 100,
+ },
+ default: 50,
+ description: 'How many results to return',
+ },
+ {
+ displayName: 'Simplify Output',
+ name: 'simple',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'search',
+ ],
+ },
+ },
+ default: true,
+ description: 'Whether to return a simplified version of the response instead of the raw data',
+ },
+ {
+ displayName: 'Options',
+ name: 'options',
+ type: 'collection',
+ displayOptions: {
+ show: {
+ resource: [
+ 'database',
+ ],
+ operation: [
+ 'search',
+ ],
+ },
+ },
+ default: {},
+ placeholder: 'Add Field',
+ options: [
+ {
+ displayName: 'Sort',
+ name: 'sort',
+ placeholder: 'Add Sort',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: false,
+ },
+ default: {},
+ options: [
+ {
+ displayName: 'Sort',
+ name: 'sortValue',
+ values: [
+ {
+ displayName: 'Direction',
+ name: 'direction',
+ type: 'options',
+ options: [
+ {
+ name: 'Ascending',
+ value: 'ascending',
+ },
+ {
+ name: 'Descending',
+ value: 'descending',
+ },
+ ],
+ default: 'descending',
+ description: 'The direction to sort',
+ },
+ {
+ displayName: 'Timestamp',
+ name: 'timestamp',
+ type: 'options',
+ options: [
+ {
+ name: 'Last Edited Time',
+ value: 'last_edited_time',
+ },
+ ],
+ default: 'last_edited_time',
+ description: `The name of the timestamp to sort against`,
+ },
+ ],
+ },
+ ],
+ },
+ ],
},
-];
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts b/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts
index eaf861ccaedd3..c300bcf635e50 100644
--- a/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts
+++ b/packages/nodes-base/nodes/Notion/DatabasePageDescription.ts
@@ -2,6 +2,11 @@ import {
INodeProperties,
} from 'n8n-workflow';
+import {
+ getConditions,
+ getSearchFilters,
+} from './GenericFunctions';
+
import {
blocks,
text,
@@ -11,13 +16,16 @@ import {
filters,
} from './Filters';
-export const databasePageOperations: INodeProperties[] = [
+export const databasePageOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
+ version: [
+ 2,
+ ],
resource: [
'databasePage',
],
@@ -29,6 +37,11 @@ export const databasePageOperations: INodeProperties[] = [
value: 'create',
description: 'Create a pages in a database',
},
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a page in a database',
+ },
{
name: 'Get All',
value: 'getAll',
@@ -41,17 +54,49 @@ export const databasePageOperations: INodeProperties[] = [
},
],
default: 'create',
- description: 'The operation to perform.',
},
-];
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ version: [
+ 1,
+ ],
+ resource: [
+ 'databasePage',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a pages in a database',
+ },
+ {
+ name: 'Get All',
+ value: 'getAll',
+ description: 'Get all pages in a database',
+ },
+ {
+ name: 'Update',
+ value: 'update',
+ description: 'Update pages in a database',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
-export const databasePageFields: INodeProperties[] = [
+export const databasePageFields = [
/* -------------------------------------------------------------------------- */
- /* databasePage:create */
+ /* databasePage:create */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Database ID',
+ displayName: 'Database Name or ID',
name: 'databaseId',
type: 'options',
default: '',
@@ -69,10 +114,30 @@ export const databasePageFields: INodeProperties[] = [
],
},
},
- description: 'The ID of the database that this databasePage belongs to.',
+ description: `The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
},
{
- displayName: 'Simple',
+ displayName: 'Title',
+ name: 'title',
+ type: 'string',
+ default: '',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'databasePage',
+ ],
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ description: 'Page title. Appears at the top of the page and can be found via Quick Find',
+ },
+ {
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
@@ -86,7 +151,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
{
displayName: 'Properties',
@@ -194,7 +259,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: `Phone number. No structure is enforced.`,
+ description: `Phone number. No structure is enforced`,
},
{
displayName: 'Options',
@@ -212,7 +277,7 @@ export const databasePageFields: INodeProperties[] = [
},
default: [],
description: `Name of the options you want to set.
- Multiples can be defined separated by comma.`,
+ Multiples can be defined separated by comma`,
},
{
displayName: 'Option',
@@ -229,7 +294,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: `Name of the option you want to set.`,
+ description: `Name of the option you want to set`,
},
{
displayName: 'Email',
@@ -243,7 +308,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: 'Email address.',
+ description: 'Email address',
},
{
displayName: 'URL',
@@ -257,7 +322,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: 'Web address.',
+ description: 'Web address',
},
{
displayName: 'User IDs',
@@ -274,7 +339,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: [],
- description: 'List of users. Multiples can be defined separated by comma.',
+ description: 'List of users. Multiples can be defined separated by comma',
},
{
displayName: 'Relation IDs',
@@ -291,7 +356,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: [],
- description: 'List of databases that belong to another database. Multiples can be defined separated by comma.',
+ description: 'List of databases that belong to another database. Multiples can be defined separated by comma',
},
{
displayName: 'Checked',
@@ -319,7 +384,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'number',
default: 0,
- description: 'Number value.',
+ description: 'Number value',
},
{
displayName: 'Range',
@@ -333,7 +398,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'boolean',
default: false,
- description: 'Weather or not you want to define a date range.',
+ description: 'Weather or not you want to define a date range',
},
{
displayName: 'Include Time',
@@ -347,7 +412,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'boolean',
default: true,
- description: 'Weather or not to include the time in the date.',
+ description: 'Weather or not to include the time in the date',
},
{
displayName: 'Date',
@@ -364,7 +429,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date Start',
@@ -381,7 +446,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date End',
@@ -399,7 +464,7 @@ export const databasePageFields: INodeProperties[] = [
type: 'dateTime',
default: '',
description: `
- An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
+ An ISO 8601 formatted date, with optional time. Represents the end of a date range`,
},
{
displayName: 'Timezone',
@@ -416,7 +481,49 @@ export const databasePageFields: INodeProperties[] = [
loadOptionsMethod: 'getTimezones',
},
default: 'default',
- description: 'Time zone to use. By default n8n timezone is used.',
+ description: 'Time zone to use. By default n8n timezone is used',
+ },
+ {
+ displayName: 'File URLs',
+ name: 'fileUrls',
+ placeholder: 'Add File',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ sortable: true,
+ },
+ displayOptions: {
+ show: {
+ '/version': [
+ 2,
+ ],
+ type: [
+ 'files',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ name: 'fileUrl',
+ displayName: 'File',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'File URL',
+ name: 'url',
+ type: 'string',
+ default: '',
+ description: 'Link to externally hosted file',
+ },
+ ],
+ },
+ ],
},
],
},
@@ -424,10 +531,10 @@ export const databasePageFields: INodeProperties[] = [
},
...blocks('databasePage', 'create'),
/* -------------------------------------------------------------------------- */
- /* databasePage:update */
+ /* databasePage:update */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Page ID',
+ displayName: 'Database Page Link or ID',
name: 'pageId',
type: 'string',
default: '',
@@ -442,10 +549,10 @@ export const databasePageFields: INodeProperties[] = [
],
},
},
- description: 'The ID of the databasePage to update.',
+ description: `The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
},
{
- displayName: 'Simplify Response',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
@@ -459,7 +566,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: true,
- description: 'Return a simplified version of the response instead of the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
{
displayName: 'Properties',
@@ -567,7 +674,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: `Phone number. No structure is enforced.`,
+ description: `Phone number. No structure is enforced`,
},
{
displayName: 'Options',
@@ -584,8 +691,6 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: [],
- description: `Name of the options you want to set.
- Multiples can be defined separated by comma.`,
},
{
displayName: 'Option',
@@ -602,7 +707,6 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: `Name of the option you want to set.`,
},
{
displayName: 'Email',
@@ -616,7 +720,6 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: 'Email address.',
},
{
displayName: 'URL',
@@ -630,7 +733,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: 'Web address.',
+ description: 'Web address',
},
{
displayName: 'User IDs',
@@ -647,7 +750,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: [],
- description: 'List of users. Multiples can be defined separated by comma.',
+ description: 'List of users. Multiples can be defined separated by comma',
},
{
displayName: 'Relation IDs',
@@ -664,7 +767,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: [],
- description: 'List of databases that belong to another database. Multiples can be defined separated by comma.',
+ description: 'List of databases that belong to another database. Multiples can be defined separated by comma',
},
{
displayName: 'Checked',
@@ -692,7 +795,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'number',
default: 0,
- description: 'Number value.',
+ description: 'Number value',
},
{
displayName: 'Range',
@@ -706,7 +809,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'boolean',
default: false,
- description: 'Weather or not you want to define a date range.',
+ description: 'Weather or not you want to define a date range',
},
{
displayName: 'Include Time',
@@ -720,7 +823,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'boolean',
default: true,
- description: 'Weather or not to include the time in the date.',
+ description: 'Weather or not to include the time in the date',
},
{
displayName: 'Date',
@@ -737,7 +840,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date Start',
@@ -754,7 +857,7 @@ export const databasePageFields: INodeProperties[] = [
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Date End',
@@ -772,7 +875,7 @@ export const databasePageFields: INodeProperties[] = [
type: 'dateTime',
default: '',
description: `
- An ISO 8601 formatted date, with optional time. Represents the end of a date range.`,
+ An ISO 8601 formatted date, with optional time. Represents the end of a date range`,
},
{
displayName: 'Timezone',
@@ -789,17 +892,103 @@ export const databasePageFields: INodeProperties[] = [
loadOptionsMethod: 'getTimezones',
},
default: 'default',
- description: 'Time zone to use. By default n8n timezone is used.',
+ description: 'Time zone to use. By default n8n timezone is used',
+ },
+ {
+ displayName: 'File URLs',
+ name: 'fileUrls',
+ placeholder: 'Add File',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ sortable: true,
+ },
+ displayOptions: {
+ show: {
+ '/version': [
+ 2,
+ ],
+ type: [
+ 'files',
+ ],
+ },
+ },
+ default: {},
+ options: [
+ {
+ name: 'fileUrl',
+ displayName: 'File',
+ values: [
+ {
+ displayName: 'Name',
+ name: 'name',
+ type: 'string',
+ default: '',
+ },
+ {
+ displayName: 'File URL',
+ name: 'url',
+ type: 'string',
+ default: '',
+ description: 'Link to externally hosted file',
+ },
+ ],
+ },
+ ],
},
],
},
],
},
/* -------------------------------------------------------------------------- */
+ /* databasePage:get */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Database Page Link or ID',
+ name: 'pageId',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'databasePage',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ description: `The Database Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
+ },
+ {
+ displayName: 'Simplify Output',
+ name: 'simple',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'databasePage',
+ ],
+ operation: [
+ 'get',
+ ],
+ },
+ },
+ default: true,
+ description: 'Whether to return a simplified version of the response instead of the raw data',
+ },
+ /* -------------------------------------------------------------------------- */
/* databasePage:getAll */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Database ID',
+ displayName: 'Database Name or ID',
name: 'databaseId',
type: 'options',
typeOptions: {
@@ -833,7 +1022,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: false,
- description: 'If all results should be returned or only up to a given limit.',
+ description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
@@ -857,10 +1046,10 @@ export const databasePageFields: INodeProperties[] = [
maxValue: 100,
},
default: 50,
- description: 'How many results to return.',
+ description: 'How many results to return',
},
{
- displayName: 'Simple',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
@@ -874,8 +1063,9 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
+ ...getSearchFilters('databasePage'),
{
displayName: 'Options',
name: 'options',
@@ -893,6 +1083,26 @@ export const databasePageFields: INodeProperties[] = [
default: {},
placeholder: 'Add Field',
options: [
+ {
+ displayName: 'Download Files',
+ name: 'downloadFiles',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ '/version': [
+ 2,
+ ],
+ '/resource': [
+ 'databasePage',
+ ],
+ '/operation': [
+ 'getAll',
+ ],
+ },
+ },
+ default: false,
+ description: 'If a database field contains a file, whether to download it',
+ },
{
displayName: 'Filters',
name: 'filter',
@@ -901,13 +1111,20 @@ export const databasePageFields: INodeProperties[] = [
typeOptions: {
multipleValues: false,
},
+ displayOptions: {
+ show: {
+ '/version': [
+ 1,
+ ],
+ },
+ },
default: {},
options: [
{
displayName: 'Single Condition',
name: 'singleCondition',
values: [
- ...filters,
+ ...filters(getConditions()),
],
},
{
@@ -928,14 +1145,14 @@ export const databasePageFields: INodeProperties[] = [
displayName: 'OR',
name: 'or',
values: [
- ...filters,
+ ...filters(getConditions()),
],
},
{
displayName: 'AND',
name: 'and',
values: [
- ...filters,
+ ...filters(getConditions()),
],
},
],
@@ -963,7 +1180,7 @@ export const databasePageFields: INodeProperties[] = [
name: 'timestamp',
type: 'boolean',
default: false,
- description: `Whether or not to use the record's timestamp to sort the response.`,
+ description: `Whether or not to use the record's timestamp to sort the response`,
},
{
displayName: 'Property Name',
@@ -983,7 +1200,7 @@ export const databasePageFields: INodeProperties[] = [
],
},
default: '',
- description: 'The name of the property to filter by.',
+ description: 'The name of the property to filter by',
},
{
displayName: 'Property Name',
@@ -1007,7 +1224,7 @@ export const databasePageFields: INodeProperties[] = [
},
},
default: '',
- description: 'The name of the property to filter by.',
+ description: 'The name of the property to filter by',
},
{
displayName: 'Type',
@@ -1037,7 +1254,7 @@ export const databasePageFields: INodeProperties[] = [
},
],
default: '',
- description: 'The direction to sort.',
+ description: 'The direction to sort',
},
],
},
@@ -1045,4 +1262,4 @@ export const databasePageFields: INodeProperties[] = [
},
],
},
-];
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Notion/Filters.ts b/packages/nodes-base/nodes/Notion/Filters.ts
index 4d55760d4793d..f0d8e20b9e03c 100644
--- a/packages/nodes-base/nodes/Notion/Filters.ts
+++ b/packages/nodes-base/nodes/Notion/Filters.ts
@@ -1,12 +1,7 @@
-import {
- INodeProperties
-} from 'n8n-workflow';
-import {
- getConditions
-} from './GenericFunctions';
-export const filters: INodeProperties[] = [{
+// tslint:disable-next-line: no-any
+export const filters = (conditions: any) => [{
displayName: 'Property Name',
name: 'key',
type: 'options',
@@ -17,7 +12,7 @@ export const filters: INodeProperties[] = [{
],
},
default: '',
- description: 'The name of the property to filter by.',
+ description: 'The name of the property to filter by',
},
{
displayName: 'Type',
@@ -25,7 +20,7 @@ export const filters: INodeProperties[] = [{
type: 'hidden',
default: '={{$parameter["&key"].split("|")[1]}}',
},
-...getConditions(),
+...conditions,
{
displayName: 'Title',
name: 'titleValue',
@@ -82,7 +77,7 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: `Phone number. No structure is enforced.`,
+ description: `Phone number. No structure is enforced`,
},
{
displayName: 'Option',
@@ -105,8 +100,6 @@ export const filters: INodeProperties[] = [{
},
},
default: [],
- description: `Name of the options you want to set.
- Multiples can be defined separated by comma.`,
},
{
displayName: 'Option',
@@ -129,7 +122,6 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: `Name of the option you want to set.`,
},
{
displayName: 'Email',
@@ -149,7 +141,6 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: 'Email address.',
},
{
displayName: 'URL',
@@ -169,7 +160,6 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: 'Web address.',
},
{
displayName: 'User ID',
@@ -192,7 +182,7 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: 'List of users. Multiples can be defined separated by comma.',
+ description: 'List of users. Multiples can be defined separated by comma',
},
{
displayName: 'User ID',
@@ -215,7 +205,7 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: 'List of users. Multiples can be defined separated by comma.',
+ description: 'List of users. Multiples can be defined separated by comma',
},
{
displayName: 'User ID',
@@ -238,7 +228,7 @@ export const filters: INodeProperties[] = [{
},
},
default: '',
- description: 'List of users. Multiples can be defined separated by comma.',
+ description: 'List of users. Multiples can be defined separated by comma',
},
{
displayName: 'Relation ID',
@@ -271,7 +261,7 @@ export const filters: INodeProperties[] = [{
},
type: 'boolean',
default: false,
- description: 'Whether or not the checkbox is checked. true
represents checked. false
represents unchecked.',
+ description: 'Whether or not the checkbox is checked. true
represents checked. false
represents unchecked',
},
{
displayName: 'Number',
@@ -291,7 +281,7 @@ export const filters: INodeProperties[] = [{
},
type: 'number',
default: 0,
- description: 'Number value.',
+ description: 'Number value',
},
{
displayName: 'Date',
@@ -317,7 +307,7 @@ export const filters: INodeProperties[] = [{
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Created Time',
@@ -343,7 +333,7 @@ export const filters: INodeProperties[] = [{
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
+ description: 'An ISO 8601 format date, with optional time',
},
{
displayName: 'Last Edited Time',
@@ -369,5 +359,99 @@ export const filters: INodeProperties[] = [{
},
type: 'dateTime',
default: '',
- description: 'An ISO 8601 format date, with optional time.',
-}];
+ description: 'An ISO 8601 format date, with optional time',
+},
+//formula types
+{
+ displayName: 'Number',
+ name: 'numberValue',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ returnType: [
+ 'number',
+ ],
+ },
+ hide: {
+ condition: [
+ 'is_empty',
+ 'is_not_empty',
+ ],
+ },
+ },
+ type: 'number',
+ default: 0,
+ description: 'Number value',
+},
+{
+ displayName: 'Text',
+ name: 'textValue',
+ type: 'string',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ returnType: [
+ 'text',
+ ],
+ },
+ hide: {
+ condition: [
+ 'is_empty',
+ 'is_not_empty',
+ ],
+ },
+ },
+ default: '',
+},
+{
+ displayName: 'Boolean',
+ name: 'checkboxValue',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ returnType: [
+ 'checkbox',
+ ],
+ },
+ },
+ type: 'boolean',
+ default: false,
+ description: 'Whether or not the checkbox is checked. true
represents checked. false
represents unchecked',
+
+},
+{
+ displayName: 'Date',
+ name: 'dateValue',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ returnType: [
+ 'date',
+ ],
+ },
+ hide: {
+ condition: [
+ 'is_empty',
+ 'is_not_empty',
+ 'past_week',
+ 'past_month',
+ 'past_year',
+ 'next_week',
+ 'next_month',
+ 'next_year',
+ ],
+ },
+ },
+ type: 'dateTime',
+ default: '',
+ description: 'An ISO 8601 format date, with optional time',
+},
+];
\ No newline at end of file
diff --git a/packages/nodes-base/nodes/Notion/GenericFunctions.ts b/packages/nodes-base/nodes/Notion/GenericFunctions.ts
index 77f60e65902f2..0f6709d8a8fa7 100644
--- a/packages/nodes-base/nodes/Notion/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/Notion/GenericFunctions.ts
@@ -10,8 +10,12 @@ import {
} from 'n8n-core';
import {
+ IBinaryKeyData,
+ ICredentialDataDecryptedObject,
+ ICredentialTestFunctions,
IDataObject,
IDisplayOptions,
+ INodeExecutionData,
INodeProperties,
IPollFunctions,
NodeApiError,
@@ -22,16 +26,27 @@ import {
capitalCase,
} from 'change-case';
+import {
+ filters,
+} from './Filters';
+
import * as moment from 'moment-timezone';
import { validate as uuidValidate } from 'uuid';
+import { snakeCase } from 'change-case';
+
+const apiVersion: { [key: number]: string } = {
+ 1: '2021-05-13',
+ 2: '2021-08-16',
+};
+
export async function notionApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any
try {
let options: OptionsWithUri = {
headers: {
- 'Notion-Version': '2021-05-13',
+ 'Notion-Version': apiVersion[this.getNode().typeVersion],
},
method,
qs,
@@ -39,10 +54,15 @@ export async function notionApiRequest(this: IHookFunctions | IExecuteFunctions
uri: uri || `https://api.notion.com/v1${resource}`,
json: true,
};
-
options = Object.assign({}, options, option);
const credentials = await this.getCredentials('notionApi') as IDataObject;
- options!.headers!['Authorization'] = `Bearer ${credentials.apiKey}`;
+ if (!uri) {
+ //do not include the API Key when downloading files, else the request fails
+ options!.headers!['Authorization'] = `Bearer ${credentials.apiKey}`;
+ }
+ if (Object.keys(body).length === 0) {
+ delete options.body;
+ }
return this.helpers.request!(options);
} catch (error) {
@@ -209,7 +229,7 @@ export function formatBlocks(blocks: IDataObject[]) {
object: 'block',
type: block.type,
[block.type as string]: {
- ...(block.type === 'to_do') ? { checked: block.checked } : { checked: false },
+ ...(block.type === 'to_do') ? { checked: block.checked } : {},
//@ts-expect-error
// tslint:disable-next-line: no-any
text: (block.richText === false) ? formatText(block.textContent).text : getTexts(block.text.text as any || []),
@@ -220,7 +240,7 @@ export function formatBlocks(blocks: IDataObject[]) {
}
// tslint:disable-next-line: no-any
-function getPropertyKeyValue(value: any, type: string, timezone: string) {
+function getPropertyKeyValue(value: any, type: string, timezone: string, version = 1) {
let result = {};
switch (type) {
case 'rich_text':
@@ -268,6 +288,11 @@ function getPropertyKeyValue(value: any, type: string, timezone: string) {
};
break;
case 'people':
+ //if expression it's a single value, make it an array
+ if (!Array.isArray(value.peopleValue)) {
+ value.peopleValue = [value.peopleValue];
+ }
+
result = {
type: 'people', people: value.peopleValue.map((option: string) => ({ id: option })),
};
@@ -279,7 +304,7 @@ function getPropertyKeyValue(value: any, type: string, timezone: string) {
break;
case 'select':
result = {
- type: 'select', select: { id: value.selectValue },
+ type: 'select', select: (version === 1) ? { id: value.selectValue } : { name: value.selectValue },
};
break;
case 'date':
@@ -302,6 +327,20 @@ function getPropertyKeyValue(value: any, type: string, timezone: string) {
},
};
}
+
+ //if the date was left empty, set it to null so it resets the value in notion
+ if (value.date === '' ||
+ (value.dateStart === '' && value.dateEnd === '')) {
+ //@ts-ignore
+ result.date = null;
+ }
+
+ break;
+ case 'files':
+ result = {
+ type: 'files', files: value.fileUrls.fileUrl
+ .map((file: { name: string, url: string }) => ({ name: file.name, type: 'external', external: { url: file.url } })),
+ };
break;
default:
}
@@ -323,9 +362,9 @@ function getNameAndType(key: string) {
};
}
-export function mapProperties(properties: IDataObject[], timezone: string) {
+export function mapProperties(properties: IDataObject[], timezone: string, version = 1) {
return properties.reduce((obj, value) => Object.assign(obj, {
- [`${(value.key as string).split('|')[0]}`]: getPropertyKeyValue(value, (value.key as string).split('|')[1], timezone),
+ [`${(value.key as string).split('|')[0]}`]: getPropertyKeyValue(value, (value.key as string).split('|')[1], timezone, version),
}), {});
}
@@ -339,6 +378,7 @@ export function mapSorting(data: [{ key: string, type: string, direction: string
}
export function mapFilters(filters: IDataObject[], timezone: string) {
+
// tslint:disable-next-line: no-any
return filters.reduce((obj, value: { [key: string]: any }) => {
let key = getNameAndType(value.key).type;
@@ -352,12 +392,25 @@ export function mapFilters(filters: IDataObject[], timezone: string) {
} else if (['past_week', 'past_month', 'past_year', 'next_week', 'next_month', 'next_year'].includes(value.condition as string)) {
valuePropertyName = {};
}
- if (key === 'rich_text') {
+ if (key === 'rich_text' || key === 'text') {
key = 'text';
} else if (key === 'phone_number') {
key = 'phone';
} else if (key === 'date' && !['is_empty', 'is_not_empty'].includes(value.condition as string)) {
valuePropertyName = (valuePropertyName !== undefined && !Object.keys(valuePropertyName).length) ? {} : moment.tz(value.date, timezone).utc().format();
+ } else if (key === 'number') {
+ key = 'text';
+ } else if (key === 'boolean') {
+ key = 'checkbox';
+ }
+
+ if (value.type === 'formula') {
+ const valuePropertyName = value[`${camelCase(value.returnType)}Value`];
+
+ return Object.assign(obj, {
+ ['property']: getNameAndType(value.key).name,
+ [key]: { [value.returnType]: { [`${value.condition}`]: valuePropertyName } },
+ });
}
return Object.assign(obj, {
@@ -389,11 +442,11 @@ export function simplifyProperties(properties: any) {
results[`${key}`] = '';
}
} else if (['created_by', 'last_edited_by', 'select'].includes(properties[key].type)) {
- results[`${key}`] = properties[key][type].name;
+ results[`${key}`] = (properties[key][type]) ? properties[key][type].name : null;
} else if (['people'].includes(properties[key].type)) {
if (Array.isArray(properties[key][type])) {
// tslint:disable-next-line: no-any
- results[`${key}`] = properties[key][type].map((person: any) => person.person.email || {});
+ results[`${key}`] = properties[key][type].map((person: any) => person.person?.email || {});
} else {
results[`${key}`] = properties[key][type];
}
@@ -415,32 +468,49 @@ export function simplifyProperties(properties: any) {
} else if (['rollup'].includes(properties[key].type)) {
//TODO figure how to resolve rollup field type
// results[`${key}`] = properties[key][type][properties[key][type].type];
+ } else if (['files'].includes(properties[key].type)) {
+ // tslint:disable-next-line: no-any
+ results[`${key}`] = properties[key][type].map((file: { type: string, [key: string]: any }) => (file[file.type].url));
}
}
return results;
}
// tslint:disable-next-line: no-any
-export function simplifyObjects(objects: any) {
+export function simplifyObjects(objects: any, download = false, version = 2) {
if (!Array.isArray(objects)) {
objects = [objects];
}
const results: IDataObject[] = [];
- for (const { object, id, properties, parent, title } of objects) {
+ for (const { object, id, properties, parent, title, json, binary, url, created_time, last_edited_time } of objects) {
if (object === 'page' && (parent.type === 'page_id' || parent.type === 'workspace')) {
results.push({
id,
- title: properties.title.title[0].plain_text,
+ name: properties.title.title[0].plain_text,
+ ...version === 2 ? { url } : {},
});
} else if (object === 'page' && parent.type === 'database_id') {
results.push({
id,
- ...simplifyProperties(properties),
+ ...(version === 2) ? { name: getPropertyTitle(properties) } : {},
+ ...(version === 2) ? { url } : {},
+ ...(version === 2) ? { ...prepend('property', simplifyProperties(properties)) } : { ...simplifyProperties(properties) },
+ });
+ } else if (download && json.object === 'page' && json.parent.type === 'database_id') {
+ results.push({
+ json: {
+ id,
+ ...(version === 2) ? { name: getPropertyTitle(json.properties) } : {},
+ ...(version === 2) ? { url } : {},
+ ...(version === 2) ? { ...prepend('property', simplifyProperties(json.properties)) } : { ...simplifyProperties(json.properties) },
+ },
+ binary,
});
} else if (object === 'database') {
results.push({
id,
- title: title[0].plain_text,
+ ...version === 2 ? { name: title[0]?.plain_text || '' } : { title: title[0]?.plain_text || '' },
+ ...version === 2 ? { url } : {},
});
}
}
@@ -549,11 +619,20 @@ export function getConditions() {
'is_empty',
'is_not_empty',
],
- formula: [
- 'contains',
- 'does_not_contain',
- 'is_empty',
- 'is_not_empty',
+ };
+
+ const formula: { [key: string]: string[] } = {
+ text: [
+ ...typeConditions.rich_text,
+ ],
+ checkbox: [
+ ...typeConditions.checkbox,
+ ],
+ number: [
+ ...typeConditions.number,
+ ],
+ date: [
+ ...typeConditions.date,
],
};
@@ -576,5 +655,283 @@ export function getConditions() {
} as INodeProperties,
);
}
+
+ elements.push(
+ {
+ displayName: 'Return Type',
+ name: 'returnType',
+ type: 'options',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ },
+ } as IDisplayOptions,
+ options: Object.keys(formula).map((key: string) => ({ name: capitalCase(key), value: key })),
+ default: '',
+ description: 'The formula return type',
+ } as INodeProperties,
+ );
+
+ for (const key of Object.keys(formula)) {
+ elements.push(
+ {
+ displayName: 'Condition',
+ name: 'condition',
+ type: 'options',
+ displayOptions: {
+ show: {
+ type: [
+ 'formula',
+ ],
+ returnType: [
+ key,
+ ],
+ },
+ } as IDisplayOptions,
+ options: formula[key].map((key: string) => ({ name: capitalCase(key), value: key })),
+ default: '',
+ description: 'The value of the property to filter by.',
+ } as INodeProperties,
+ );
+ }
+
+ return elements;
+}
+
+export function validateCrendetials(this: ICredentialTestFunctions, credentials: ICredentialDataDecryptedObject) {
+ const options: OptionsWithUri = {
+ headers: {
+ 'Authorization': `Bearer ${credentials.apiKey}`,
+ 'Notion-Version': apiVersion[2],
+ },
+ method: 'GET',
+ uri: `https://api.notion.com/v1/users/me`,
+ json: true,
+ };
+ return this.helpers.request!(options);
+}
+
+// tslint:disable-next-line: no-any
+export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: [{ properties: { [key: string]: any | { id: string, type: string, files: [{ external: { url: string } } | { file: { url: string } }] } } }]): Promise {
+ const elements: INodeExecutionData[] = [];
+ for (const record of records) {
+ const element: INodeExecutionData = { json: {}, binary: {} };
+ element.json = record as unknown as IDataObject;
+ for (const key of Object.keys(record.properties)) {
+ if (record.properties[key].type === 'files') {
+ if (record.properties[key].files.length) {
+ for (const [index, file] of record.properties[key].files.entries()) {
+ const data = await notionApiRequest.call(this, 'GET', '', {}, {}, file?.file?.url || file?.external?.url, { json: false, encoding: null });
+ element.binary![`${key}_${index}`] = await this.helpers.prepareBinaryData(data);
+ }
+ }
+ }
+ }
+ if (Object.keys(element.binary as IBinaryKeyData).length === 0) {
+ delete element.binary;
+ }
+ elements.push(element);
+ }
return elements;
}
+
+export function extractPageId(page: string) {
+ if (page.includes('p=')) {
+ return page.split('p=')[1];
+ } else if (page.includes('-') && page.includes('https')) {
+ return page.split('-')[page.split('-').length - 1];
+ }
+ return page;
+}
+
+export function extractDatabaseId(database: string) {
+ if (database.includes('?v=')) {
+ const data = database.split('?v=')[0].split('/');
+ const index = data.length - 1;
+ return data[index];
+ } else if (database.includes('/')) {
+ const index = database.split('/').length - 1;
+ return database.split('/')[index];
+ } else {
+ return database;
+ }
+}
+
+// tslint:disable-next-line: no-any
+function prepend(stringKey: string, properties: { [key: string]: any }) {
+ for (const key of Object.keys(properties)) {
+ properties[`${stringKey}_${snakeCase(key)}`] = properties[key];
+ delete properties[key];
+ }
+ return properties;
+}
+
+// tslint:disable-next-line: no-any
+export function getPropertyTitle(properties: { [key: string]: any }) {
+ return Object.values(properties).filter(property => property.type === 'title')[0].title[0]?.plain_text || '';
+}
+
+export function getSearchFilters(resource: string) {
+ return [
+ {
+ displayName: 'Filter',
+ name: 'filterType',
+ type: 'options',
+ options: [
+ {
+ name: 'None',
+ value: 'none',
+ },
+ {
+ name: 'Build Manually',
+ value: 'manual',
+ },
+ {
+ name: 'JSON',
+ value: 'json',
+ },
+ ],
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ resource,
+ ],
+ operation: [
+ 'getAll',
+ ],
+ },
+ },
+ default: 'none',
+ },
+ {
+ displayName: 'Must Match',
+ name: 'matchType',
+ type: 'options',
+ options: [
+ {
+ name: 'Any filter',
+ value: 'anyFilter',
+ },
+ {
+ name: 'All Filters',
+ value: 'allFilters',
+ },
+ ],
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ resource,
+ ],
+ operation: [
+ 'getAll',
+ ],
+ filterType: [
+ 'manual',
+ ],
+ },
+ },
+ default: 'anyFilter',
+ },
+ {
+ displayName: 'Filters',
+ name: 'filters',
+ type: 'fixedCollection',
+ typeOptions: {
+ multipleValues: true,
+ },
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ resource,
+ ],
+ operation: [
+ 'getAll',
+ ],
+ filterType: [
+ 'manual',
+ ],
+ },
+ },
+ default: '',
+ placeholder: 'Add Condition',
+ options: [
+ {
+ displayName: 'Conditions',
+ name: 'conditions',
+ values: [
+ ...filters(getConditions()),
+ ],
+ },
+ ],
+ },
+ {
+ displayName: 'See Notion guide to creating filters',
+ name: 'jsonNotice',
+ type: 'notice',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ resource,
+ ],
+ operation: [
+ 'getAll',
+ ],
+ filterType: [
+ 'json',
+ ],
+ },
+ },
+ default: '',
+ },
+ {
+ displayName: 'Filters (JSON)',
+ name: 'filterJson',
+ type: 'string',
+ typeOptions: {
+ alwaysOpenEditWindow: true,
+ },
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ resource,
+ ],
+ operation: [
+ 'getAll',
+ ],
+ filterType: [
+ 'json',
+ ],
+ },
+ },
+ default: '',
+ description: '',
+ },
+ ];
+}
+
+export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
+ let result;
+ try {
+ result = JSON.parse(json!);
+ } catch (exception) {
+ result = undefined;
+ }
+ return result;
+}
\ No newline at end of file
diff --git a/packages/nodes-base/nodes/Notion/Notion.node.ts b/packages/nodes-base/nodes/Notion/Notion.node.ts
index 602cb83035ffe..1c8970c0819bb 100644
--- a/packages/nodes-base/nodes/Notion/Notion.node.ts
+++ b/packages/nodes-base/nodes/Notion/Notion.node.ts
@@ -1,547 +1,37 @@
import {
- IExecuteFunctions,
-} from 'n8n-core';
-
-import {
- IDataObject,
- ILoadOptionsFunctions,
- INodeExecutionData,
- INodePropertyOptions,
- INodeType,
- INodeTypeDescription,
+ INodeTypeBaseDescription,
+ INodeVersionedType,
} from 'n8n-workflow';
import {
- formatBlocks,
- formatTitle,
- getBlockTypes,
- mapFilters,
- mapProperties,
- mapSorting,
- notionApiRequest,
- notionApiRequestAllItems,
- simplifyObjects,
-} from './GenericFunctions';
+ NotionV1,
+} from './v1/NotionV1.node';
import {
- databaseFields,
- databaseOperations,
-} from './DatabaseDescription';
+ NotionV2,
+} from './v2/NotionV2.node';
import {
- userFields,
- userOperations,
-} from './UserDescription';
-
-import {
- pageFields,
- pageOperations,
-} from './PageDescription';
-
-import {
- blockFields,
- blockOperations,
-} from './BlockDescription';
-
-import {
- databasePageFields,
- databasePageOperations,
-} from './DatabasePageDescription';
-
-import * as moment from 'moment-timezone';
-
-export class Notion implements INodeType {
- description: INodeTypeDescription = {
- displayName: 'Notion (Beta)',
- name: 'notion',
- icon: 'file:notion.svg',
- group: ['output'],
- version: 1,
- subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
- description: 'Consume Notion API (Beta)',
- defaults: {
- name: 'Notion',
- },
- inputs: ['main'],
- outputs: ['main'],
- credentials: [
- {
- name: 'notionApi',
- required: true,
- // displayOptions: {
- // show: {
- // authentication: [
- // 'apiKey',
- // ],
- // },
- // },
- },
- // {
- // name: 'notionOAuth2Api',
- // required: true,
- // displayOptions: {
- // show: {
- // authentication: [
- // 'oAuth2',
- // ],
- // },
- // },
- // },
- ],
- properties: [
- // {
- // displayName: 'Authentication',
- // name: 'authentication',
- // type: 'options',
- // options: [
- // {
- // name: 'API Key',
- // value: 'apiKey',
- // },
- // {
- // name: 'OAuth2',
- // value: 'oAuth2',
- // },
- // ],
- // default: 'apiKey',
- // description: 'The resource to operate on.',
- // },
- {
- displayName: 'Resource',
- name: 'resource',
- type: 'options',
- options: [
- {
- name: 'Block',
- value: 'block',
- },
- {
- name: 'Database',
- value: 'database',
- },
- {
- name: 'Database Page',
- value: 'databasePage',
- },
- {
- name: 'Page',
- value: 'page',
- },
- {
- name: 'User',
- value: 'user',
- },
- ],
- default: 'page',
- description: 'Resource to consume.',
- },
- ...blockOperations,
- ...blockFields,
- ...databaseOperations,
- ...databaseFields,
- ...databasePageOperations,
- ...databasePageFields,
- ...pageOperations,
- ...pageFields,
- ...userOperations,
- ...userFields,
- ],
- };
-
- methods = {
- 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 { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
- for (const key of Object.keys(properties)) {
- //remove parameters that cannot be set from the API.
- if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files'].includes(properties[key].type)) {
- returnData.push({
- name: `${key} - (${properties[key].type})`,
- value: `${key}|${properties[key].type}`,
- });
- }
- }
- 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 getFilterProperties(this: ILoadOptionsFunctions): Promise {
- const returnData: INodePropertyOptions[] = [];
- const databaseId = this.getCurrentNodeParameter('databaseId') as string;
- const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
- for (const key of Object.keys(properties)) {
- returnData.push({
- name: `${key} - (${properties[key].type})`,
- value: `${key}|${properties[key].type}`,
- });
- }
- 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 getBlockTypes(this: ILoadOptionsFunctions): Promise {
- return getBlockTypes();
- },
- async getPropertySelectValues(this: ILoadOptionsFunctions): Promise {
- const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
- const databaseId = this.getCurrentNodeParameter('databaseId') 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') {
- return (properties[name][type].options)
- .map((option: IDataObject) => ({ name: option.name, value: option.name }));
- } else if (['multi_select'].includes(type) && ['create', 'update'].includes(operation)) {
- return (properties[name][type].options)
- .map((option: IDataObject) => ({ name: option.name, value: option.name }));
- }
- }
- return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
- },
- async getUsers(this: ILoadOptionsFunctions): Promise {
- const returnData: INodePropertyOptions[] = [];
- const users = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
- for (const user of users) {
- returnData.push({
- name: user.name,
- value: user.id,
- });
- }
- return returnData;
- },
- async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise {
- const returnData: INodePropertyOptions[] = [];
- const pageId = this.getCurrentNodeParameter('pageId') as string;
- const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
- 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.
- if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files'].includes(properties[key].type)) {
- returnData.push({
- name: `${key} - (${properties[key].type})`,
- value: `${key}|${properties[key].type}`,
- });
- }
- }
- 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 getDatabaseOptionsFromPage(this: ILoadOptionsFunctions): Promise {
- const pageId = this.getCurrentNodeParameter('pageId') as string;
- const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
- const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
- const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
- return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
- },
-
- // Get all the timezones to display them to user so that he can
- // select them easily
- async getTimezones(
- this: ILoadOptionsFunctions,
- ): Promise {
- const returnData: INodePropertyOptions[] = [];
- for (const timezone of moment.tz.names()) {
- const timezoneName = timezone;
- const timezoneId = timezone;
- returnData.push({
- name: timezoneName,
- value: timezoneId,
- });
- }
- returnData.unshift({
- name: 'Default',
- value: 'default',
- description: 'Timezone set in n8n',
- });
- return returnData;
- },
- },
- };
-
- async execute(this: IExecuteFunctions): Promise {
- const items = this.getInputData();
- const returnData: IDataObject[] = [];
- const length = items.length as unknown as number;
- let responseData;
- const qs: IDataObject = {};
- const timezone = this.getTimezone();
-
- const resource = this.getNodeParameter('resource', 0) as string;
- const operation = this.getNodeParameter('operation', 0) as string;
-
- if (resource === 'block') {
-
- if (operation === 'append') {
- for (let i = 0; i < length; i++) {
- const blockId = this.getNodeParameter('blockId', i) as string;
- const body: IDataObject = {
- children: formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]),
- };
- const block = await notionApiRequest.call(this, 'PATCH', `/blocks/${blockId}/children`, body);
- returnData.push(block);
- }
- }
-
- if (operation === 'getAll') {
- for (let i = 0; i < length; i++) {
- const blockId = this.getNodeParameter('blockId', i) as string;
- const returnAll = this.getNodeParameter('returnAll', i) as boolean;
- if (returnAll) {
- responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', `/blocks/${blockId}/children`, {});
- } else {
- qs.page_size = this.getNodeParameter('limit', i) as number;
- responseData = await notionApiRequest.call(this, 'GET', `/blocks/${blockId}/children`, {});
- responseData = responseData.results;
- }
- returnData.push.apply(returnData, responseData);
- }
- }
- }
-
- if (resource === 'database') {
-
- if (operation === 'get') {
- for (let i = 0; i < length; i++) {
- const databaseId = this.getNodeParameter('databaseId', i) as string;
- responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
- returnData.push(responseData);
- }
- }
-
- if (operation === 'getAll') {
- for (let i = 0; i < length; i++) {
- const body: IDataObject = {
- filter: { property: 'object', value: 'database' },
- };
- const returnAll = this.getNodeParameter('returnAll', i) as boolean;
- if (returnAll) {
- responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/search`, body);
- } else {
- body['page_size'] = this.getNodeParameter('limit', i) as number;
- responseData = await notionApiRequest.call(this, 'POST', `/search`, body);
- responseData = responseData.results;
- }
- returnData.push.apply(returnData, responseData);
- }
- }
- }
-
- if (resource === 'databasePage') {
-
- if (operation === 'create') {
- for (let i = 0; i < length; i++) {
- const simple = this.getNodeParameter('simple', i) as boolean;
- // tslint:disable-next-line: no-any
- const body: { [key: string]: any } = {
- parent: {},
- properties: {},
- };
- body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string;
- const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
- if (properties.length !== 0) {
- body.properties = mapProperties(properties, timezone) as IDataObject;
- }
- body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
- responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
- returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
- }
- }
-
- 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 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[];
- const body: IDataObject = {
- filter: {},
- };
- if (filters.singleCondition) {
- body['filter'] = mapFilters([filters.singleCondition] as IDataObject[], timezone);
- }
- if (filters.multipleCondition) {
- const { or, and } = (filters.multipleCondition as IDataObject).condition as IDataObject;
- if (Array.isArray(or) && or.length !== 0) {
- Object.assign(body.filter, { or: (or as IDataObject[]).map((data) => mapFilters([data], timezone)) });
- }
- if (Array.isArray(and) && and.length !== 0) {
- Object.assign(body.filter, { and: (and as IDataObject[]).map((data) => mapFilters([data], timezone)) });
- }
- }
- if (!Object.keys(body.filter as IDataObject).length) {
- delete body.filter;
- }
- if (sort) {
- //@ts-expect-error
- body['sorts'] = mapSorting(sort);
- }
- if (returnAll) {
- responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/databases/${databaseId}/query`, body, {});
- } else {
- body.page_size = this.getNodeParameter('limit', i) as number;
- responseData = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body, qs);
- responseData = responseData.results;
- }
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
- returnData.push.apply(returnData, responseData);
- }
- }
-
- if (operation === 'update') {
- for (let i = 0; i < length; i++) {
- const pageId = this.getNodeParameter('pageId', i) as string;
- const simple = this.getNodeParameter('simple', i) as boolean;
- const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
- // tslint:disable-next-line: no-any
- const body: { [key: string]: any } = {
- properties: {},
- };
- if (properties.length !== 0) {
- body.properties = mapProperties(properties, timezone) as IDataObject;
- }
- responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, body);
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
- returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
- }
- }
- }
-
- if (resource === 'user') {
-
- if (operation === 'get') {
- for (let i = 0; i < length; i++) {
- const userId = this.getNodeParameter('userId', i) as string;
- responseData = await notionApiRequest.call(this, 'GET', `/users/${userId}`);
- returnData.push(responseData);
- }
- }
- if (operation === 'getAll') {
- for (let i = 0; i < length; i++) {
- const returnAll = this.getNodeParameter('returnAll', i) as boolean;
- if (returnAll) {
- responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
- } else {
- qs.limit = this.getNodeParameter('limit', i) as number;
- responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
- responseData = responseData.splice(0, qs.limit);
- }
- returnData.push.apply(returnData, responseData);
- }
- }
- }
-
- if (resource === 'page') {
-
- if (operation === 'create') {
- for (let i = 0; i < length; i++) {
- const simple = this.getNodeParameter('simple', i) as boolean;
- // tslint:disable-next-line: no-any
- const body: { [key: string]: any } = {
- parent: {},
- properties: {},
- };
- body.parent['page_id'] = this.getNodeParameter('pageId', i) as string;
- body.properties = formatTitle(this.getNodeParameter('title', i) as string);
- body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
- responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
- returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
- }
- }
-
- if (operation === 'get') {
- for (let i = 0; i < length; i++) {
- const pageId = this.getNodeParameter('pageId', i) as string;
- const simple = this.getNodeParameter('simple', i) as boolean;
- responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
- returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
- }
- }
-
- if (operation === 'search') {
- for (let i = 0; i < length; i++) {
- const text = this.getNodeParameter('text', i) as string;
- const options = this.getNodeParameter('options', i) as IDataObject;
- const returnAll = this.getNodeParameter('returnAll', i) as boolean;
- const simple = this.getNodeParameter('simple', i) as boolean;
- const body: IDataObject = {};
-
- if (text) {
- body['query'] = text;
- }
-
- if (options.filter) {
- const filter = (options.filter as IDataObject || {}).filters as IDataObject[] || [];
- body['filter'] = filter;
- }
+ NodeVersionedType,
+} from '../../src/NodeVersionedType';
- if (options.sort) {
- const sort = (options.sort as IDataObject || {}).sortValue as IDataObject || {};
- body['sort'] = sort;
- }
- if (returnAll) {
- responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
- } else {
- qs.limit = this.getNodeParameter('limit', i) as number;
- responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
- responseData = responseData.splice(0, qs.limit);
- }
+export class Notion extends NodeVersionedType {
+ constructor() {
+ const baseDescription: INodeTypeBaseDescription = {
+ displayName: 'Notion (Beta)',
+ name: 'notion',
+ icon: 'file:notion.svg',
+ group: ['output'],
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Notion API (Beta)',
+ defaultVersion: 2,
+ };
- if (simple === true) {
- responseData = simplifyObjects(responseData);
- }
+ const nodeVersions: INodeVersionedType['nodeVersions'] = {
+ 1: new NotionV1(baseDescription),
+ 2: new NotionV2(baseDescription),
+ };
- returnData.push.apply(returnData, responseData);
- }
- }
- }
- return [this.helpers.returnJsonArray(returnData)];
+ super(nodeVersions, baseDescription);
}
-}
+}
\ No newline at end of file
diff --git a/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts b/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts
index 236ab0a9ca61b..ecaa6e3de7d91 100644
--- a/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts
+++ b/packages/nodes-base/nodes/Notion/NotionTrigger.node.ts
@@ -34,6 +34,7 @@ export class NotionTrigger implements INodeType {
{
name: 'notionApi',
required: true,
+ testedBy: 'notionApiCredentialTest',
},
],
polling: true,
@@ -49,10 +50,10 @@ export class NotionTrigger implements INodeType {
name: 'Page Added to Database',
value: 'pageAddedToDatabase',
},
- // {
- // name: 'Record Updated',
- // value: 'recordUpdated',
- // },
+ {
+ name: 'Paged Updated in Database',
+ value: 'pagedUpdatedInDatabase',
+ },
],
required: true,
default: '',
@@ -68,26 +69,28 @@ export class NotionTrigger implements INodeType {
show: {
event: [
'pageAddedToDatabase',
+ 'pagedUpdatedInDatabase',
],
},
},
default: '',
required: true,
- description: 'The ID of this database.',
+ description: 'The ID of this database',
},
{
- displayName: 'Simple',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
show: {
event: [
'pageAddedToDatabase',
+ 'pagedUpdatedInDatabase',
],
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
],
};
@@ -148,7 +151,7 @@ export class NotionTrigger implements INodeType {
if (this.getMode() === 'manual') {
if (simple === true) {
- data = simplifyObjects(data);
+ data = simplifyObjects(data, false, 1);
}
if (Array.isArray(data) && data.length) {
return [this.helpers.returnJsonArray(data)];
@@ -172,7 +175,7 @@ export class NotionTrigger implements INodeType {
}
if (simple === true) {
- records = simplifyObjects(records);
+ records = simplifyObjects(records, false, 1);
}
webhookData.lastRecordProccesed = data[0].id;
diff --git a/packages/nodes-base/nodes/Notion/PageDescription.ts b/packages/nodes-base/nodes/Notion/PageDescription.ts
index c75277a6e9be2..1898e57150ed8 100644
--- a/packages/nodes-base/nodes/Notion/PageDescription.ts
+++ b/packages/nodes-base/nodes/Notion/PageDescription.ts
@@ -6,13 +6,16 @@ import {
blocks,
} from './Blocks';
-export const pageOperations: INodeProperties[] = [
+export const pageOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
+ version: [
+ 1,
+ ],
resource: [
'page',
],
@@ -36,17 +39,93 @@ export const pageOperations: INodeProperties[] = [
},
],
default: 'create',
- description: 'The operation to perform.',
},
-];
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'page',
+ ],
+ },
+ },
+ options: [
+ {
+ name: 'Archive',
+ value: 'archive',
+ description: 'Archive a page',
+ },
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a page',
+ },
+ {
+ name: 'Search',
+ value: 'search',
+ description: 'Text search of pages',
+ },
+ ],
+ default: 'create',
+ },
+] as INodeProperties[];
-export const pageFields: INodeProperties[] = [
+export const pageFields = [
+ /* -------------------------------------------------------------------------- */
+ /* page:archive */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Page Link or ID',
+ name: 'pageId',
+ type: 'string',
+ default: '',
+ required: true,
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'page',
+ ],
+ operation: [
+ 'archive',
+ ],
+ },
+ },
+ description: `The Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
+ },
+ {
+ displayName: 'Simplify Output',
+ name: 'simple',
+ type: 'boolean',
+ displayOptions: {
+ show: {
+ version: [
+ 2,
+ ],
+ resource: [
+ 'page',
+ ],
+ operation: [
+ 'archive',
+ ],
+ },
+ },
+ default: true,
+ description: 'Whether to return a simplified version of the response instead of the raw data',
+ },
/* -------------------------------------------------------------------------- */
/* page:create */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Parent Page ID',
+ displayName: 'Parent Page ID or Link',
name: 'pageId',
type: 'string',
default: '',
@@ -61,7 +140,7 @@ export const pageFields: INodeProperties[] = [
],
},
},
- description: 'The ID of the parent page that this child page belongs to.',
+ description: `The URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
},
{
displayName: 'Title',
@@ -79,10 +158,10 @@ export const pageFields: INodeProperties[] = [
],
},
},
- description: 'Page title. Appears at the top of the page and can be found via Quick Find.',
+ description: 'Page title. Appears at the top of the page and can be found via Quick Find',
},
{
- displayName: 'Simple',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
@@ -96,20 +175,23 @@ export const pageFields: INodeProperties[] = [
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
...blocks('page', 'create'),
/* -------------------------------------------------------------------------- */
/* page:get */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Page ID',
+ displayName: 'Page Link or ID',
name: 'pageId',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
+ version: [
+ 1,
+ ],
resource: [
'page',
],
@@ -118,13 +200,17 @@ export const pageFields: INodeProperties[] = [
],
},
},
+ description: `The Page URL from Notion's 'copy link' functionality (or just the ID contained within the URL)`,
},
{
- displayName: 'Simple',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
show: {
+ version: [
+ 1,
+ ],
resource: [
'page',
],
@@ -134,7 +220,7 @@ export const pageFields: INodeProperties[] = [
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
/* -------------------------------------------------------------------------- */
/* page:search */
@@ -154,7 +240,7 @@ export const pageFields: INodeProperties[] = [
],
},
},
- description: 'The text to search for.',
+ description: 'The text to search for',
},
{
displayName: 'Return All',
@@ -171,7 +257,7 @@ export const pageFields: INodeProperties[] = [
},
},
default: false,
- description: 'If all results should be returned or only up to a given limit.',
+ description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
@@ -195,10 +281,10 @@ export const pageFields: INodeProperties[] = [
maxValue: 100,
},
default: 50,
- description: 'How many results to return.',
+ description: 'How many results to return',
},
{
- displayName: 'Simple',
+ displayName: 'Simplify Output',
name: 'simple',
type: 'boolean',
displayOptions: {
@@ -212,7 +298,7 @@ export const pageFields: INodeProperties[] = [
},
},
default: true,
- description: 'When set to true a simplify version of the response will be used else the raw data.',
+ description: 'Whether to return a simplified version of the response instead of the raw data',
},
{
displayName: 'Options',
@@ -256,7 +342,7 @@ export const pageFields: INodeProperties[] = [
},
],
default: 'object',
- description: 'The name of the property to filter by.',
+ description: 'The name of the property to filter by',
},
{
displayName: 'Value',
@@ -273,7 +359,7 @@ export const pageFields: INodeProperties[] = [
},
],
default: '',
- description: 'The value of the property to filter by.',
+ description: 'The value of the property to filter by',
},
],
},
@@ -307,8 +393,8 @@ export const pageFields: INodeProperties[] = [
value: 'descending',
},
],
- default: '',
- description: 'The direction to sort.',
+ default: 'descending',
+ description: 'The direction to sort',
},
{
displayName: 'Timestamp',
@@ -321,7 +407,7 @@ export const pageFields: INodeProperties[] = [
},
],
default: 'last_edited_time',
- description: `The name of the timestamp to sort against.`,
+ description: `The name of the timestamp to sort against`,
},
],
},
@@ -329,4 +415,4 @@ export const pageFields: INodeProperties[] = [
},
],
},
-];
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Notion/UserDescription.ts b/packages/nodes-base/nodes/Notion/UserDescription.ts
index b5fc5c012f32a..2e582b29ea180 100644
--- a/packages/nodes-base/nodes/Notion/UserDescription.ts
+++ b/packages/nodes-base/nodes/Notion/UserDescription.ts
@@ -2,7 +2,7 @@ import {
INodeProperties,
} from 'n8n-workflow';
-export const userOperations: INodeProperties[] = [
+export const userOperations = [
{
displayName: 'Operation',
name: 'operation',
@@ -27,11 +27,10 @@ export const userOperations: INodeProperties[] = [
},
],
default: 'get',
- description: 'The operation to perform.',
},
-];
+] as INodeProperties[];
-export const userFields: INodeProperties[] = [
+export const userFields = [
/* -------------------------------------------------------------------------- */
/* user:get */
@@ -71,7 +70,7 @@ export const userFields: INodeProperties[] = [
},
},
default: false,
- description: 'If all results should be returned or only up to a given limit.',
+ description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
@@ -95,6 +94,6 @@ export const userFields: INodeProperties[] = [
maxValue: 100,
},
default: 50,
- description: 'How many results to return.',
+ description: 'How many results to return',
},
-];
+] as INodeProperties[];
diff --git a/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts b/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts
new file mode 100644
index 0000000000000..89d78682c3e52
--- /dev/null
+++ b/packages/nodes-base/nodes/Notion/v1/NotionV1.node.ts
@@ -0,0 +1,444 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+
+import {
+ IDataObject,
+ ILoadOptionsFunctions,
+ INodeExecutionData,
+ INodePropertyOptions,
+ INodeType,
+ INodeTypeBaseDescription,
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+import {
+ extractDatabaseId,
+ extractPageId,
+ formatBlocks,
+ formatTitle,
+ getBlockTypes,
+ mapFilters,
+ mapProperties,
+ mapSorting,
+ notionApiRequest,
+ notionApiRequestAllItems,
+ simplifyObjects,
+} from '../GenericFunctions';
+
+import * as moment from 'moment-timezone';
+
+import {
+ versionDescription
+} from './VersionDescription';
+
+export class NotionV1 implements INodeType {
+
+ description: INodeTypeDescription;
+
+ constructor(baseDescription: INodeTypeBaseDescription) {
+ this.description = {
+ ...baseDescription,
+ ...versionDescription,
+ };
+ }
+
+ methods = {
+ 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 { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ for (const key of Object.keys(properties)) {
+ //remove parameters that cannot be set from the API.
+ if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files', 'rollup'].includes(properties[key].type)) {
+ returnData.push({
+ name: `${key} - (${properties[key].type})`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ }
+ 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 getFilterProperties(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const databaseId = this.getCurrentNodeParameter('databaseId') as string;
+ const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ for (const key of Object.keys(properties)) {
+ returnData.push({
+ name: `${key} - (${properties[key].type})`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ 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 getBlockTypes(this: ILoadOptionsFunctions): Promise {
+ return getBlockTypes();
+ },
+ async getPropertySelectValues(this: ILoadOptionsFunctions): Promise {
+ const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
+ const databaseId = this.getCurrentNodeParameter('databaseId') 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') {
+ return (properties[name][type].options)
+ .map((option: IDataObject) => ({ name: option.name, value: option.name }));
+ } else if (['multi_select'].includes(type) && ['create', 'update'].includes(operation)) {
+ return (properties[name][type].options)
+ .map((option: IDataObject) => ({ name: option.name, value: option.name }));
+ }
+ }
+ return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
+ },
+ async getUsers(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const users = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ for (const user of users) {
+ returnData.push({
+ name: user.name,
+ value: user.id,
+ });
+ }
+ return returnData;
+ },
+ async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const pageId = this.getCurrentNodeParameter('pageId') as string;
+ const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ 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.
+ if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files'].includes(properties[key].type)) {
+ returnData.push({
+ name: `${key} - (${properties[key].type})`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ }
+ 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 getDatabaseOptionsFromPage(this: ILoadOptionsFunctions): Promise {
+ const pageId = this.getCurrentNodeParameter('pageId') as string;
+ const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
+ const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
+ },
+
+ // Get all the timezones to display them to user so that he can
+ // select them easily
+ async getTimezones(
+ this: ILoadOptionsFunctions,
+ ): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ for (const timezone of moment.tz.names()) {
+ const timezoneName = timezone;
+ const timezoneId = timezone;
+ returnData.push({
+ name: timezoneName,
+ value: timezoneId,
+ });
+ }
+ returnData.unshift({
+ name: 'Default',
+ value: 'default',
+ description: 'Timezone set in n8n',
+ });
+ return returnData;
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+ const length = items.length as unknown as number;
+ let responseData;
+ const qs: IDataObject = {};
+ const timezone = this.getTimezone();
+
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+
+ if (resource === 'block') {
+
+ if (operation === 'append') {
+ for (let i = 0; i < length; i++) {
+ const blockId = extractPageId(this.getNodeParameter('blockId', i) as string);
+ const body: IDataObject = {
+ children: formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]),
+ };
+ const block = await notionApiRequest.call(this, 'PATCH', `/blocks/${blockId}/children`, body);
+ returnData.push(block);
+ }
+ }
+
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const blockId = extractPageId(this.getNodeParameter('blockId', i) as string);
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', `/blocks/${blockId}/children`, {});
+ } else {
+ qs.page_size = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'GET', `/blocks/${blockId}/children`, {}, qs);
+ responseData = responseData.results;
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+
+
+ if (resource === 'database') {
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const databaseId = extractDatabaseId(this.getNodeParameter('databaseId', i) as string);
+ responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const body: IDataObject = {
+ filter: { property: 'object', value: 'database' },
+ };
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/search`, body);
+ } else {
+ body['page_size'] = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'POST', `/search`, body);
+ responseData = responseData.results;
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+ if (resource === 'databasePage') {
+
+ if (operation === 'create') {
+ for (let i = 0; i < length; i++) {
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ parent: {},
+ properties: {},
+ };
+ body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string;
+ const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
+ if (properties.length !== 0) {
+ body.properties = mapProperties(properties, timezone) as IDataObject;
+ }
+ body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
+ responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ 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 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[];
+ const body: IDataObject = {
+ filter: {},
+ };
+ if (filters.singleCondition) {
+ body['filter'] = mapFilters([filters.singleCondition] as IDataObject[], timezone);
+ }
+ if (filters.multipleCondition) {
+ const { or, and } = (filters.multipleCondition as IDataObject).condition as IDataObject;
+ if (Array.isArray(or) && or.length !== 0) {
+ Object.assign(body.filter, { or: (or as IDataObject[]).map((data) => mapFilters([data], timezone)) });
+ }
+ if (Array.isArray(and) && and.length !== 0) {
+ Object.assign(body.filter, { and: (and as IDataObject[]).map((data) => mapFilters([data], timezone)) });
+ }
+ }
+ if (!Object.keys(body.filter as IDataObject).length) {
+ delete body.filter;
+ }
+ if (sort) {
+ //@ts-expect-error
+ body['sorts'] = mapSorting(sort);
+ }
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/databases/${databaseId}/query`, body, {});
+ } else {
+ body.page_size = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body, qs);
+ responseData = responseData.results;
+ }
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+
+ if (operation === 'update') {
+ for (let i = 0; i < length; i++) {
+ const pageId = extractPageId(this.getNodeParameter('pageId', i) as string);
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ properties: {},
+ };
+ if (properties.length !== 0) {
+ body.properties = mapProperties(properties, timezone) as IDataObject;
+ }
+ responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+ }
+
+ if (resource === 'user') {
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const userId = this.getNodeParameter('userId', i) as string;
+ responseData = await notionApiRequest.call(this, 'GET', `/users/${userId}`);
+ returnData.push(responseData);
+ }
+ }
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ responseData = responseData.splice(0, qs.limit);
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+ if (resource === 'page') {
+
+ if (operation === 'create') {
+ for (let i = 0; i < length; i++) {
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ parent: {},
+ properties: {},
+ };
+ body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i) as string);
+ body.properties = formatTitle(this.getNodeParameter('title', i) as string);
+ body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
+ responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const pageId = extractPageId(this.getNodeParameter('pageId', i) as string);
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'search') {
+ for (let i = 0; i < length; i++) {
+ const text = this.getNodeParameter('text', i) as string;
+ const options = this.getNodeParameter('options', i) as IDataObject;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ const body: IDataObject = {};
+
+ if (text) {
+ body['query'] = text;
+ }
+
+ if (options.filter) {
+ const filter = (options.filter as IDataObject || {}).filters as IDataObject[] || [];
+ body['filter'] = filter;
+ }
+
+ if (options.sort) {
+ const sort = (options.sort as IDataObject || {}).sortValue as IDataObject || {};
+ body['sort'] = sort;
+ }
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ responseData = responseData.splice(0, qs.limit);
+ }
+
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false, 1);
+ }
+
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+}
diff --git a/packages/nodes-base/nodes/Notion/v1/VersionDescription.ts b/packages/nodes-base/nodes/Notion/v1/VersionDescription.ts
new file mode 100644
index 0000000000000..68dee6e8939f4
--- /dev/null
+++ b/packages/nodes-base/nodes/Notion/v1/VersionDescription.ts
@@ -0,0 +1,137 @@
+import {
+ databaseFields,
+ databaseOperations,
+} from '../DatabaseDescription';
+
+import {
+ userFields,
+ userOperations,
+} from '../UserDescription';
+
+import {
+ pageFields,
+ pageOperations,
+} from '../PageDescription';
+
+import {
+ blockFields,
+ blockOperations,
+} from '../BlockDescription';
+
+import {
+ databasePageFields,
+ databasePageOperations,
+} from '../DatabasePageDescription';
+
+import {
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+export const versionDescription: INodeTypeDescription = {
+ displayName: 'Notion (Beta)',
+ name: 'notion',
+ icon: 'file:notion.svg',
+ group: ['output'],
+ version: 1,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Notion API (Beta)',
+ defaults: {
+ name: 'Notion',
+ color: '#000000',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'notionApi',
+ required: true,
+ // displayOptions: {
+ // show: {
+ // authentication: [
+ // 'apiKey',
+ // ],
+ // },
+ // },
+ },
+ // {
+ // name: 'notionOAuth2Api',
+ // required: true,
+ // displayOptions: {
+ // show: {
+ // authentication: [
+ // 'oAuth2',
+ // ],
+ // },
+ // },
+ // },
+ ],
+ properties: [
+ // {
+ // displayName: 'Authentication',
+ // name: 'authentication',
+ // type: 'options',
+ // options: [
+ // {
+ // name: 'API Key',
+ // value: 'apiKey',
+ // },
+ // {
+ // name: 'OAuth2',
+ // value: 'oAuth2',
+ // },
+ // ],
+ // default: 'apiKey',
+ // description: 'The resource to operate on.',
+ // },
+ {
+ displayName: 'To access content, make sure it\'s shared with your integration in Notion',
+ name: 'notionNotice',
+ type: 'notice',
+ default: '',
+ },
+ {
+ displayName: 'Version',
+ name: 'version',
+ type: 'hidden',
+ default: 1,
+ },
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Block',
+ value: 'block',
+ },
+ {
+ name: 'Database',
+ value: 'database',
+ },
+ {
+ name: 'Database Page',
+ value: 'databasePage',
+ },
+ {
+ name: 'Page',
+ value: 'page',
+ },
+ {
+ name: 'User',
+ value: 'user',
+ },
+ ],
+ default: 'page',
+ },
+ ...blockOperations,
+ ...blockFields,
+ ...databaseOperations,
+ ...databaseFields,
+ ...databasePageOperations,
+ ...databasePageFields,
+ ...pageOperations,
+ ...pageFields,
+ ...userOperations,
+ ...userFields,
+ ],
+ };
diff --git a/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts
new file mode 100644
index 0000000000000..c609f7669ba76
--- /dev/null
+++ b/packages/nodes-base/nodes/Notion/v2/NotionV2.node.ts
@@ -0,0 +1,556 @@
+import {
+ IExecuteFunctions,
+} from 'n8n-core';
+
+import {
+ ICredentialDataDecryptedObject,
+ ICredentialsDecrypted,
+ ICredentialTestFunctions,
+ IDataObject,
+ ILoadOptionsFunctions,
+ INodeExecutionData,
+ INodePropertyOptions,
+ INodeType,
+ INodeTypeBaseDescription,
+ INodeTypeDescription,
+ NodeApiError,
+ NodeCredentialTestResult,
+} from 'n8n-workflow';
+
+import {
+ downloadFiles,
+ extractDatabaseId,
+ extractPageId,
+ formatBlocks,
+ formatTitle,
+ getBlockTypes,
+ mapFilters,
+ mapProperties,
+ mapSorting,
+ notionApiRequest,
+ notionApiRequestAllItems,
+ simplifyObjects,
+ validateCrendetials,
+ validateJSON,
+} from '../GenericFunctions';
+
+import * as moment from 'moment-timezone';
+
+import {
+ versionDescription
+} from './VersionDescription';
+
+export class NotionV2 implements INodeType {
+
+ description: INodeTypeDescription;
+
+ constructor(baseDescription: INodeTypeBaseDescription) {
+ this.description = {
+ ...baseDescription,
+ ...versionDescription,
+ };
+ }
+
+ methods = {
+ 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 { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ for (const key of Object.keys(properties)) {
+ //remove parameters that cannot be set from the API.
+ if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files', 'rollup'].includes(properties[key].type)) {
+ returnData.push({
+ name: `${key}`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ }
+ 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 getFilterProperties(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const databaseId = this.getCurrentNodeParameter('databaseId') as string;
+ const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ for (const key of Object.keys(properties)) {
+ returnData.push({
+ name: `${key}`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ 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 getBlockTypes(this: ILoadOptionsFunctions): Promise {
+ return getBlockTypes();
+ },
+ async getPropertySelectValues(this: ILoadOptionsFunctions): Promise {
+ const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
+ const databaseId = this.getCurrentNodeParameter('databaseId') 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') {
+ return (properties[name][type].options)
+ .map((option: IDataObject) => ({ name: option.name, value: option.name }));
+ } else if (['multi_select', 'select'].includes(type) && ['create', 'update'].includes(operation)) {
+ return (properties[name][type].options)
+ .map((option: IDataObject) => ({ name: option.name, value: option.name }));
+ }
+ }
+ return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.id }));
+ },
+ async getUsers(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const users = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ for (const user of users) {
+ returnData.push({
+ name: user.name,
+ value: user.id,
+ });
+ }
+ return returnData;
+ },
+ async getDatabaseIdFromPage(this: ILoadOptionsFunctions): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ const pageId = extractPageId(this.getCurrentNodeParameter('pageId') as string);
+ const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ 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.
+ if (!['created_time', 'last_edited_time', 'created_by', 'last_edited_by', 'formula', 'files', 'rollup'].includes(properties[key].type)) {
+ returnData.push({
+ name: `${key}`,
+ value: `${key}|${properties[key].type}`,
+ });
+ }
+ }
+ 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 getDatabaseOptionsFromPage(this: ILoadOptionsFunctions): Promise {
+ const pageId = extractPageId(this.getCurrentNodeParameter('pageId') as string);
+ const [name, type] = (this.getCurrentNodeParameter('&key') as string).split('|');
+ const { parent: { database_id: databaseId } } = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ return (properties[name][type].options).map((option: IDataObject) => ({ name: option.name, value: option.name }));
+ },
+
+ // Get all the timezones to display them to user so that he can
+ // select them easily
+ async getTimezones(
+ this: ILoadOptionsFunctions,
+ ): Promise {
+ const returnData: INodePropertyOptions[] = [];
+ for (const timezone of moment.tz.names()) {
+ const timezoneName = timezone;
+ const timezoneId = timezone;
+ returnData.push({
+ name: timezoneName,
+ value: timezoneId,
+ });
+ }
+ returnData.unshift({
+ name: 'Default',
+ value: 'default',
+ description: 'Timezone set in n8n',
+ });
+ return returnData;
+ },
+ },
+ credentialTest: {
+ async notionApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise {
+ try {
+ await validateCrendetials.call(this, credential.data as ICredentialDataDecryptedObject);
+ } catch (error) {
+ return {
+ status: 'Error',
+ message: 'The security token included in the request is invalid',
+ };
+ }
+
+ return {
+ status: 'OK',
+ message: 'Connection successful!',
+ };
+ },
+ },
+ };
+
+ async execute(this: IExecuteFunctions): Promise {
+ const items = this.getInputData();
+ const returnData: IDataObject[] = [];
+ const length = items.length as unknown as number;
+ let responseData;
+ const qs: IDataObject = {};
+ const timezone = this.getTimezone();
+
+ const resource = this.getNodeParameter('resource', 0) as string;
+ const operation = this.getNodeParameter('operation', 0) as string;
+ let download = false;
+
+ if (resource === 'block') {
+
+ if (operation === 'append') {
+ for (let i = 0; i < length; i++) {
+ const blockId = extractPageId(this.getNodeParameter('blockId', i) as string);
+ const body: IDataObject = {
+ children: formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]),
+ };
+ const block = await notionApiRequest.call(this, 'PATCH', `/blocks/${blockId}/children`, body);
+ returnData.push(block);
+ }
+ }
+
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const blockId = extractPageId(this.getNodeParameter('blockId', i) as string);
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', `/blocks/${blockId}/children`, {});
+ } else {
+ qs.page_size = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'GET', `/blocks/${blockId}/children`, {}, qs);
+ responseData = responseData.results;
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+ if (resource === 'database') {
+
+ 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);
+ responseData = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download)[0];
+ }
+ returnData.push(responseData);
+ }
+ }
+
+ if (operation === 'getAll') {
+ const simple = this.getNodeParameter('simple', 0) as boolean;
+ for (let i = 0; i < length; i++) {
+ const body: IDataObject = {
+ filter: { property: 'object', value: 'database' },
+ };
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/search`, body);
+ } else {
+ body['page_size'] = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'POST', `/search`, body);
+ responseData = responseData.results;
+ }
+
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+
+ if (operation === 'search') {
+ for (let i = 0; i < length; i++) {
+ const text = this.getNodeParameter('text', i) as string;
+ const options = this.getNodeParameter('options', i) as IDataObject;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ const body: IDataObject = {
+ filter: {
+ property: 'object',
+ value: 'database',
+ },
+ };
+
+ if (text) {
+ body['query'] = text;
+ }
+ if (options.sort) {
+ const sort = (options.sort as IDataObject || {}).sortValue as IDataObject || {};
+ body['sort'] = sort;
+ }
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ responseData = responseData.splice(0, qs.limit);
+ }
+
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+ if (resource === 'databasePage') {
+
+ if (operation === 'create') {
+ const databaseId = this.getNodeParameter('databaseId', 0) as string;
+ const { properties } = await notionApiRequest.call(this, 'GET', `/databases/${databaseId}`);
+ let titleKey = '';
+ for (const key of Object.keys(properties)) {
+ if (properties[key].type === 'title') {
+ titleKey = key;
+ }
+ }
+ for (let i = 0; i < length; i++) {
+ const title = this.getNodeParameter('title', i) as string;
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ parent: {},
+ properties: {},
+ };
+ if (title !== '') {
+ body.properties[titleKey] = {
+ title: [
+ {
+ text: {
+ content: title,
+ },
+ },
+ ],
+ };
+ }
+ body.parent['database_id'] = this.getNodeParameter('databaseId', i) as string;
+ const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
+ if (properties.length !== 0) {
+ body.properties = Object.assign(body.properties, mapProperties(properties, timezone, 2) as IDataObject);
+ }
+ body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
+ responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const pageId = extractPageId(this.getNodeParameter('pageId', i) as string);
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'getAll') {
+ 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 returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const filterType = this.getNodeParameter('filterType', 0) as string;
+ const conditions = this.getNodeParameter('filters.conditions', i, []) as IDataObject[];
+ const sort = this.getNodeParameter('options.sort.sortValue', i, []) as IDataObject[];
+ const body: IDataObject = {
+ filter: {},
+ };
+
+ if (filterType === 'manual') {
+ const matchType = this.getNodeParameter('matchType', 0) as string;
+ if (matchType === 'anyFilter') {
+ Object.assign(body.filter, { or: conditions.map((data) => mapFilters([data], timezone)) });
+ } else if (matchType === 'allFilters') {
+ Object.assign(body.filter, { and: conditions.map((data) => mapFilters([data], timezone)) });
+ }
+ } else if (filterType === 'json') {
+ const filterJson = this.getNodeParameter('filterJson', i) as string;
+ if (validateJSON(filterJson) !== undefined) {
+ body.filter = JSON.parse(filterJson);
+ } else {
+ throw new NodeApiError(this.getNode(), { message: 'Filters (JSON) must be a valid json' });
+ }
+ }
+
+ if (!Object.keys(body.filter as IDataObject).length) {
+ delete body.filter;
+ }
+ if (sort) {
+ //@ts-expect-error
+ body['sorts'] = mapSorting(sort);
+ }
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', `/databases/${databaseId}/query`, body, {});
+ } else {
+ body.page_size = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequest.call(this, 'POST', `/databases/${databaseId}/query`, body, qs);
+ responseData = responseData.results;
+ }
+ if (download === true) {
+ responseData = await downloadFiles.call(this, responseData);
+ }
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+
+ if (operation === 'update') {
+ for (let i = 0; i < length; i++) {
+ const pageId = extractPageId(this.getNodeParameter('pageId', i) as string);
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ const properties = this.getNodeParameter('propertiesUi.propertyValues', i, []) as IDataObject[];
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ properties: {},
+ };
+ if (properties.length !== 0) {
+ body.properties = mapProperties(properties, timezone, 2) as IDataObject;
+ }
+ responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, false);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+ }
+
+ if (resource === 'user') {
+
+ if (operation === 'get') {
+ for (let i = 0; i < length; i++) {
+ const userId = this.getNodeParameter('userId', i) as string;
+ responseData = await notionApiRequest.call(this, 'GET', `/users/${userId}`);
+ returnData.push(responseData);
+ }
+ }
+ if (operation === 'getAll') {
+ for (let i = 0; i < length; i++) {
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'GET', '/users');
+ responseData = responseData.splice(0, qs.limit);
+ }
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+
+ if (resource === 'page') {
+
+ if (operation === 'archive') {
+ for (let i = 0; i < length; i++) {
+ const pageId = extractPageId(this.getNodeParameter('pageId', i) as string);
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, { archived: true });
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'create') {
+ for (let i = 0; i < length; i++) {
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ // tslint:disable-next-line: no-any
+ const body: { [key: string]: any } = {
+ parent: {},
+ properties: {},
+ };
+ body.parent['page_id'] = extractPageId(this.getNodeParameter('pageId', i) as string);
+ body.properties = formatTitle(this.getNodeParameter('title', i) as string);
+ body.children = formatBlocks(this.getNodeParameter('blockUi.blockValues', i, []) as IDataObject[]);
+ responseData = await notionApiRequest.call(this, 'POST', '/pages', body);
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+ returnData.push.apply(returnData, Array.isArray(responseData) ? responseData : [responseData]);
+ }
+ }
+
+ if (operation === 'search') {
+ for (let i = 0; i < length; i++) {
+ const text = this.getNodeParameter('text', i) as string;
+ const options = this.getNodeParameter('options', i) as IDataObject;
+ const returnAll = this.getNodeParameter('returnAll', i) as boolean;
+ const simple = this.getNodeParameter('simple', i) as boolean;
+ const body: IDataObject = {};
+
+ if (text) {
+ body['query'] = text;
+ }
+ if (options.filter) {
+ const filter = (options.filter as IDataObject || {}).filters as IDataObject[] || [];
+ body['filter'] = filter;
+ }
+ if (options.sort) {
+ const sort = (options.sort as IDataObject || {}).sortValue as IDataObject || {};
+ body['sort'] = sort;
+ }
+ if (returnAll) {
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ } else {
+ qs.limit = this.getNodeParameter('limit', i) as number;
+ responseData = await notionApiRequestAllItems.call(this, 'results', 'POST', '/search', body);
+ responseData = responseData.splice(0, qs.limit);
+ }
+
+ if (simple === true) {
+ responseData = simplifyObjects(responseData, download);
+ }
+
+ returnData.push.apply(returnData, responseData);
+ }
+ }
+ }
+ if (download === true) {
+ return this.prepareOutputData(returnData as INodeExecutionData[]);
+ }
+ return [this.helpers.returnJsonArray(returnData)];
+ }
+}
diff --git a/packages/nodes-base/nodes/Notion/v2/VersionDescription.ts b/packages/nodes-base/nodes/Notion/v2/VersionDescription.ts
new file mode 100644
index 0000000000000..11718f69632d4
--- /dev/null
+++ b/packages/nodes-base/nodes/Notion/v2/VersionDescription.ts
@@ -0,0 +1,138 @@
+import {
+ databaseFields,
+ databaseOperations,
+} from '../DatabaseDescription';
+
+import {
+ userFields,
+ userOperations,
+} from '../UserDescription';
+
+import {
+ pageFields,
+ pageOperations,
+} from '../PageDescription';
+
+import {
+ blockFields,
+ blockOperations,
+} from '../BlockDescription';
+
+import {
+ databasePageFields,
+ databasePageOperations,
+} from '../DatabasePageDescription';
+
+import {
+ INodeTypeDescription,
+} from 'n8n-workflow';
+
+export const versionDescription: INodeTypeDescription = {
+ displayName: 'Notion (Beta)',
+ name: 'notion',
+ icon: 'file:notion.svg',
+ group: ['output'],
+ version: 2,
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
+ description: 'Consume Notion API (Beta)',
+ defaults: {
+ name: 'Notion',
+ color: '#000000',
+ },
+ inputs: ['main'],
+ outputs: ['main'],
+ credentials: [
+ {
+ name: 'notionApi',
+ required: true,
+ testedBy: 'notionApiCredentialTest',
+ // displayOptions: {
+ // show: {
+ // authentication: [
+ // 'apiKey',
+ // ],
+ // },
+ // },
+ },
+ // {
+ // name: 'notionOAuth2Api',
+ // required: true,
+ // displayOptions: {
+ // show: {
+ // authentication: [
+ // 'oAuth2',
+ // ],
+ // },
+ // },
+ // },
+ ],
+ properties: [
+ // {
+ // displayName: 'Authentication',
+ // name: 'authentication',
+ // type: 'options',
+ // options: [
+ // {
+ // name: 'API Key',
+ // value: 'apiKey',
+ // },
+ // {
+ // name: 'OAuth2',
+ // value: 'oAuth2',
+ // },
+ // ],
+ // default: 'apiKey',
+ // description: 'The resource to operate on.',
+ // },
+ {
+ displayName: 'To access content, make sure it\'s shared with your integration in Notion',
+ name: 'notionNotice',
+ type: 'notice',
+ default: '',
+ },
+ {
+ displayName: 'Version',
+ name: 'version',
+ type: 'hidden',
+ default: 2,
+ },
+ {
+ displayName: 'Resource',
+ name: 'resource',
+ type: 'options',
+ options: [
+ {
+ name: 'Block',
+ value: 'block',
+ },
+ {
+ name: 'Database',
+ value: 'database',
+ },
+ {
+ name: 'Database Page',
+ value: 'databasePage',
+ },
+ {
+ name: 'Page',
+ value: 'page',
+ },
+ {
+ name: 'User',
+ value: 'user',
+ },
+ ],
+ default: 'page',
+ },
+ ...blockOperations,
+ ...blockFields,
+ ...databaseOperations,
+ ...databaseFields,
+ ...databasePageOperations,
+ ...databasePageFields,
+ ...pageOperations,
+ ...pageFields,
+ ...userOperations,
+ ...userFields,
+ ],
+};