mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
fix(Notion Node): Extract page url (#11643)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions
Co-authored-by: Elias Meire <elias@meire.dev>
This commit is contained in:
parent
b0ba24cbbc
commit
cbdd535fe0
|
@ -8,6 +8,7 @@ import type {
|
||||||
ILoadOptionsFunctions,
|
ILoadOptionsFunctions,
|
||||||
INode,
|
INode,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
INodeParameterResourceLocator,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
IPairedItemData,
|
IPairedItemData,
|
||||||
IPollFunctions,
|
IPollFunctions,
|
||||||
|
@ -23,7 +24,7 @@ import moment from 'moment-timezone';
|
||||||
import { validate as uuidValidate } from 'uuid';
|
import { validate as uuidValidate } from 'uuid';
|
||||||
import set from 'lodash/set';
|
import set from 'lodash/set';
|
||||||
import { filters } from './descriptions/Filters';
|
import { filters } from './descriptions/Filters';
|
||||||
import { blockUrlExtractionRegexp } from './constants';
|
import { blockUrlExtractionRegexp, databasePageUrlValidationRegexp } from './constants';
|
||||||
|
|
||||||
function uuidValidateWithoutDashes(this: IExecuteFunctions, value: string) {
|
function uuidValidateWithoutDashes(this: IExecuteFunctions, value: string) {
|
||||||
if (uuidValidate(value)) return true;
|
if (uuidValidate(value)) return true;
|
||||||
|
@ -916,6 +917,32 @@ export function extractPageId(page = '') {
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPageId(this: IExecuteFunctions, i: number) {
|
||||||
|
const page = this.getNodeParameter('pageId', i, {}) as INodeParameterResourceLocator;
|
||||||
|
let pageId = '';
|
||||||
|
|
||||||
|
if (page.value && typeof page.value === 'string') {
|
||||||
|
if (page.mode === 'id') {
|
||||||
|
pageId = page.value;
|
||||||
|
} else if (page.value.includes('p=')) {
|
||||||
|
// e.g https://www.notion.so/xxxxx?v=xxxxx&p=xxxxx&pm=s
|
||||||
|
pageId = new URLSearchParams(page.value).get('p') || '';
|
||||||
|
} else {
|
||||||
|
// e.g https://www.notion.so/page_name-xxxxx
|
||||||
|
pageId = page.value.match(databasePageUrlValidationRegexp)?.[1] || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pageId) {
|
||||||
|
throw new NodeOperationError(
|
||||||
|
this.getNode(),
|
||||||
|
'Could not extract page ID from URL: ' + page.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageId;
|
||||||
|
}
|
||||||
|
|
||||||
export function extractDatabaseId(database: string) {
|
export function extractDatabaseId(database: string) {
|
||||||
if (database.includes('?v=')) {
|
if (database.includes('?v=')) {
|
||||||
const data = database.split('?v=')[0].split('/');
|
const data = database.split('?v=')[0].split('/');
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
import type { IExecuteFunctions, INode, INodeParameterResourceLocator } from 'n8n-workflow';
|
||||||
|
import { NodeOperationError } from 'n8n-workflow';
|
||||||
import { databasePageUrlExtractionRegexp } from '../shared/constants';
|
import { databasePageUrlExtractionRegexp } from '../shared/constants';
|
||||||
import { extractPageId, formatBlocks } from '../shared/GenericFunctions';
|
import { extractPageId, formatBlocks, getPageId } from '../shared/GenericFunctions';
|
||||||
|
import type { MockProxy } from 'jest-mock-extended';
|
||||||
|
import { mock } from 'jest-mock-extended';
|
||||||
|
|
||||||
describe('Test NotionV2, formatBlocks', () => {
|
describe('Test NotionV2, formatBlocks', () => {
|
||||||
it('should format to_do block', () => {
|
it('should format to_do block', () => {
|
||||||
|
@ -89,3 +93,113 @@ describe('Test Notion', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Test Notion, getPageId', () => {
|
||||||
|
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||||
|
const id = '3ab5bc794647496dac48feca926813fd';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return page ID directly when mode is id', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'id',
|
||||||
|
value: id,
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
|
||||||
|
const result = getPageId.call(mockExecuteFunctions, 0);
|
||||||
|
expect(result).toBe(id);
|
||||||
|
expect(mockExecuteFunctions.getNodeParameter).toHaveBeenCalledWith('pageId', 0, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract page ID from URL with p parameter', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: `https://www.notion.so/xxxxx?v=xxxxx&p=${id}&pm=s`,
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
|
||||||
|
const result = getPageId.call(mockExecuteFunctions, 0);
|
||||||
|
expect(result).toBe(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract page ID from URL using regex', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: `https://www.notion.so/page-name-${id}`,
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
|
||||||
|
const result = getPageId.call(mockExecuteFunctions, 0);
|
||||||
|
expect(result).toBe(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when page ID cannot be extracted', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: 'https://www.notion.so/invalid-url',
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ name: 'Notion', type: 'notion' }));
|
||||||
|
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(NodeOperationError);
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(
|
||||||
|
'Could not extract page ID from URL: https://www.notion.so/invalid-url',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when page value is empty', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: '',
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ name: 'Notion', type: 'notion' }));
|
||||||
|
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(NodeOperationError);
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(
|
||||||
|
'Could not extract page ID from URL: ',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when page value is undefined', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: undefined,
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ name: 'Notion', type: 'notion' }));
|
||||||
|
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(NodeOperationError);
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(
|
||||||
|
'Could not extract page ID from URL: undefined',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when page value is not a string', () => {
|
||||||
|
const page = {
|
||||||
|
mode: 'url',
|
||||||
|
value: 123 as any,
|
||||||
|
} as INodeParameterResourceLocator;
|
||||||
|
|
||||||
|
mockExecuteFunctions.getNodeParameter.mockReturnValue(page);
|
||||||
|
mockExecuteFunctions.getNode.mockReturnValue(mock<INode>({ name: 'Notion', type: 'notion' }));
|
||||||
|
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(NodeOperationError);
|
||||||
|
expect(() => getPageId.call(mockExecuteFunctions, 0)).toThrow(
|
||||||
|
'Could not extract page ID from URL: 123',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
extractBlockId,
|
extractBlockId,
|
||||||
extractDatabaseId,
|
extractDatabaseId,
|
||||||
extractDatabaseMentionRLC,
|
extractDatabaseMentionRLC,
|
||||||
extractPageId,
|
getPageId,
|
||||||
formatBlocks,
|
formatBlocks,
|
||||||
formatTitle,
|
formatTitle,
|
||||||
mapFilters,
|
mapFilters,
|
||||||
|
@ -401,9 +401,8 @@ export class NotionV2 implements INodeType {
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
for (let i = 0; i < itemsLength; i++) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
try {
|
try {
|
||||||
const pageId = extractPageId(
|
const pageId = getPageId.call(this, i);
|
||||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
|
||||||
);
|
|
||||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
||||||
if (simple) {
|
if (simple) {
|
||||||
|
@ -526,9 +525,7 @@ export class NotionV2 implements INodeType {
|
||||||
if (operation === 'update') {
|
if (operation === 'update') {
|
||||||
for (let i = 0; i < itemsLength; i++) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
try {
|
try {
|
||||||
const pageId = extractPageId(
|
const pageId = getPageId.call(this, i);
|
||||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
|
||||||
);
|
|
||||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
const properties = this.getNodeParameter(
|
const properties = this.getNodeParameter(
|
||||||
'propertiesUi.propertyValues',
|
'propertiesUi.propertyValues',
|
||||||
|
@ -635,9 +632,7 @@ export class NotionV2 implements INodeType {
|
||||||
if (operation === 'archive') {
|
if (operation === 'archive') {
|
||||||
for (let i = 0; i < itemsLength; i++) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
try {
|
try {
|
||||||
const pageId = extractPageId(
|
const pageId = getPageId.call(this, i);
|
||||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
|
||||||
);
|
|
||||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, {
|
responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, {
|
||||||
archived: true,
|
archived: true,
|
||||||
|
@ -672,9 +667,7 @@ export class NotionV2 implements INodeType {
|
||||||
parent: {},
|
parent: {},
|
||||||
properties: {},
|
properties: {},
|
||||||
};
|
};
|
||||||
body.parent.page_id = extractPageId(
|
body.parent.page_id = getPageId.call(this, i);
|
||||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
|
||||||
);
|
|
||||||
body.properties = formatTitle(this.getNodeParameter('title', i) as string);
|
body.properties = formatTitle(this.getNodeParameter('title', i) as string);
|
||||||
const blockValues = this.getNodeParameter(
|
const blockValues = this.getNodeParameter(
|
||||||
'blockUi.blockValues',
|
'blockUi.blockValues',
|
||||||
|
|
Loading…
Reference in a new issue