2022-08-17 08:50:24 -07:00
import { IPollFunctions } from 'n8n-core' ;
2021-05-20 14:31:23 -07:00
import {
IDataObject ,
ILoadOptionsFunctions ,
INodeExecutionData ,
INodePropertyOptions ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2022-08-17 08:50:24 -07:00
import { notionApiRequest , simplifyObjects } from './GenericFunctions' ;
2021-05-20 14:31:23 -07:00
2022-04-08 14:32:08 -07:00
import moment from 'moment' ;
2021-05-20 14:31:23 -07:00
export class NotionTrigger implements INodeType {
description : INodeTypeDescription = {
2022-07-04 02:12:08 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-display-name-unsuffixed-trigger-node
2021-05-20 14:31:23 -07:00
displayName : 'Notion Trigger (Beta)' ,
name : 'notionTrigger' ,
icon : 'file:notion.svg' ,
group : [ 'trigger' ] ,
version : 1 ,
description : 'Starts the workflow when Notion events occur' ,
subtitle : '={{$parameter["event"]}}' ,
defaults : {
name : 'Notion Trigger' ,
} ,
credentials : [
{
name : 'notionApi' ,
required : true ,
} ,
] ,
polling : true ,
inputs : [ ] ,
outputs : [ 'main' ] ,
properties : [
{
displayName : 'Event' ,
name : 'event' ,
type : 'options' ,
options : [
{
name : 'Page Added to Database' ,
value : 'pageAddedToDatabase' ,
} ,
2021-12-29 14:23:22 -08:00
{
2022-05-06 08:12:14 -07:00
name : 'Page Updated in Database' ,
2021-12-29 14:23:22 -08:00
value : 'pagedUpdatedInDatabase' ,
} ,
2021-05-20 14:31:23 -07:00
] ,
required : true ,
2022-11-09 02:26:13 -08:00
default : 'pageAddedToDatabase' ,
2021-05-20 14:31:23 -07:00
} ,
2022-07-04 00:41:44 -07:00
{
2022-08-17 08:50:24 -07:00
displayName :
2022-11-09 02:26:13 -08:00
'In Notion, make sure you <a href="https://www.notion.so/help/add-and-manage-connections-with-the-api#add-connections-to-pages" target="_blank">share your database with your integration</a> . Otherwise it won\'t be accessible, or listed here.' ,
2022-07-04 00:41:44 -07:00
name : 'notionNotice' ,
type : 'notice' ,
default : '' ,
} ,
2021-05-20 14:31:23 -07:00
{
2022-06-03 10:23:49 -07:00
displayName : 'Database Name or ID' ,
2021-05-20 14:31:23 -07:00
name : 'databaseId' ,
type : 'options' ,
typeOptions : {
loadOptionsMethod : 'getDatabases' ,
} ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
event : [ 'pageAddedToDatabase' , 'pagedUpdatedInDatabase' ] ,
2021-05-20 14:31:23 -07:00
} ,
} ,
default : '' ,
required : true ,
2022-08-17 08:50:24 -07:00
description :
'The ID of this database. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
2021-05-20 14:31:23 -07:00
} ,
{
2022-05-20 14:47:24 -07:00
displayName : 'Simplify' ,
2021-05-20 14:31:23 -07:00
name : 'simple' ,
type : 'boolean' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
event : [ 'pageAddedToDatabase' , 'pagedUpdatedInDatabase' ] ,
2021-05-20 14:31:23 -07:00
} ,
} ,
default : true ,
2022-08-17 08:50:24 -07:00
description :
'Whether to return a simplified version of the response instead of the raw data' ,
2021-05-20 14:31:23 -07:00
} ,
] ,
} ;
methods = {
loadOptions : {
async getDatabases ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = [ ] ;
2022-08-17 08:50:24 -07:00
const { results : databases } = await notionApiRequest . call ( this , 'POST' , ` /search ` , {
page_size : 100 ,
filter : { property : 'object' , value : 'database' } ,
} ) ;
2021-05-20 14:31:23 -07:00
for ( const database of databases ) {
returnData . push ( {
2021-09-15 01:13:11 -07:00
name : database.title [ 0 ] ? . plain_text || database . id ,
2021-05-20 14:31:23 -07:00
value : database.id ,
} ) ;
}
returnData . sort ( ( a , b ) = > {
2022-08-17 08:50:24 -07:00
if ( a . name . toLocaleLowerCase ( ) < b . name . toLocaleLowerCase ( ) ) {
return - 1 ;
}
if ( a . name . toLocaleLowerCase ( ) > b . name . toLocaleLowerCase ( ) ) {
return 1 ;
}
2021-05-20 14:31:23 -07:00
return 0 ;
} ) ;
return returnData ;
} ,
} ,
} ;
async poll ( this : IPollFunctions ) : Promise < INodeExecutionData [ ] [ ] | null > {
const webhookData = this . getWorkflowStaticData ( 'node' ) ;
const databaseId = this . getNodeParameter ( 'databaseId' ) as string ;
const event = this . getNodeParameter ( 'event' ) as string ;
const simple = this . getNodeParameter ( 'simple' ) as boolean ;
2022-11-09 02:26:13 -08:00
const lastTimeChecked = webhookData . lastTimeChecked
? moment ( webhookData . lastTimeChecked as string )
: moment ( ) . set ( { second : 0 , millisecond : 0 } ) ; // Notion timestamp accuracy is only down to the minute
2021-05-20 14:31:23 -07:00
2022-11-09 02:26:13 -08:00
// update lastTimeChecked to now
webhookData . lastTimeChecked = moment ( ) . set ( { second : 0 , millisecond : 0 } ) ;
2021-05-20 14:31:23 -07:00
2022-11-09 02:26:13 -08:00
// because Notion timestamp accuracy is only down to the minute some duplicates can be fetch
const possibleDuplicates = ( webhookData . possibleDuplicates as string [ ] ) ? ? [ ] ;
2021-05-20 14:31:23 -07:00
2022-08-17 08:50:24 -07:00
const sortProperty = event === 'pageAddedToDatabase' ? 'created_time' : 'last_edited_time' ;
2021-05-20 14:31:23 -07:00
2022-11-09 02:26:13 -08:00
const option : IDataObject = {
headers : {
'Notion-Version' : '2022-02-22' ,
} ,
} ;
2021-05-20 14:31:23 -07:00
const body : IDataObject = {
page_size : 1 ,
sorts : [
{
timestamp : sortProperty ,
direction : 'descending' ,
} ,
] ,
2022-11-09 02:26:13 -08:00
. . . ( this . getMode ( ) !== 'manual' && {
filter : {
timestamp : sortProperty ,
[ sortProperty ] : {
on_or_after : lastTimeChecked.utc ( ) . format ( ) ,
} ,
} ,
} ) ,
2021-05-20 14:31:23 -07:00
} ;
let records : IDataObject [ ] = [ ] ;
let hasMore = true ;
//get last record
2022-08-17 08:50:24 -07:00
let { results : data } = await notionApiRequest . call (
this ,
'POST' ,
` /databases/ ${ databaseId } /query ` ,
body ,
2022-11-09 02:26:13 -08:00
{ } ,
'' ,
option ,
2022-08-17 08:50:24 -07:00
) ;
2021-05-20 14:31:23 -07:00
if ( this . getMode ( ) === 'manual' ) {
if ( simple === true ) {
2021-12-29 14:23:22 -08:00
data = simplifyObjects ( data , false , 1 ) ;
2021-05-20 14:31:23 -07:00
}
if ( Array . isArray ( data ) && data . length ) {
return [ this . helpers . returnJsonArray ( data ) ] ;
}
}
// if something changed after the last check
2022-11-09 02:26:13 -08:00
if ( Array . isArray ( data ) && data . length && Object . keys ( data [ 0 ] ) . length !== 0 ) {
2021-05-20 14:31:23 -07:00
do {
body . page_size = 10 ;
2022-08-17 08:50:24 -07:00
const { results , has_more , next_cursor } = await notionApiRequest . call (
this ,
'POST' ,
` /databases/ ${ databaseId } /query ` ,
body ,
2022-11-09 02:26:13 -08:00
{ } ,
'' ,
option ,
2022-08-17 08:50:24 -07:00
) ;
2022-11-09 02:26:13 -08:00
records . push ( . . . results ) ;
2021-05-20 14:31:23 -07:00
hasMore = has_more ;
if ( next_cursor !== null ) {
body [ 'start_cursor' ] = next_cursor ;
}
2022-11-09 02:26:13 -08:00
// Only stop when we reach records strictly before last recorded time to be sure we catch records from the same minute
2022-08-17 08:50:24 -07:00
} while (
2022-11-09 02:26:13 -08:00
! moment ( records [ records . length - 1 ] [ sortProperty ] as string ) . isBefore ( lastTimeChecked ) &&
2022-08-17 08:50:24 -07:00
hasMore === true
) ;
2021-05-20 14:31:23 -07:00
2022-11-09 02:26:13 -08:00
// Filter out already processed left over records:
// with a time strictly before the last record processed
// or from the same minute not present in the list of processed records
records = records . filter (
( record : IDataObject ) = > ! possibleDuplicates . includes ( record . id as string ) ,
) ;
// Save the time of the most recent record processed
if ( records [ 0 ] ) {
const latestTimestamp = moment ( records [ 0 ] [ sortProperty ] as string ) ;
// Save record ids with the same timestamp as the latest processed records
webhookData . possibleDuplicates = records
. filter ( ( record : IDataObject ) = >
moment ( record [ sortProperty ] as string ) . isSame ( latestTimestamp ) ,
)
. map ( ( record : IDataObject ) = > record . id ) ;
} else {
webhookData . possibleDuplicates = undefined ;
2021-05-20 14:31:23 -07:00
}
if ( simple === true ) {
2021-12-29 14:23:22 -08:00
records = simplifyObjects ( records , false , 1 ) ;
2021-05-20 14:31:23 -07:00
}
if ( Array . isArray ( records ) && records . length ) {
return [ this . helpers . returnJsonArray ( records ) ] ;
}
}
return null ;
}
}