mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-28 05:59:42 -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,
|
||||
INode,
|
||||
INodeExecutionData,
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
IPairedItemData,
|
||||
IPollFunctions,
|
||||
|
@ -23,7 +24,7 @@ import moment from 'moment-timezone';
|
|||
import { validate as uuidValidate } from 'uuid';
|
||||
import set from 'lodash/set';
|
||||
import { filters } from './descriptions/Filters';
|
||||
import { blockUrlExtractionRegexp } from './constants';
|
||||
import { blockUrlExtractionRegexp, databasePageUrlValidationRegexp } from './constants';
|
||||
|
||||
function uuidValidateWithoutDashes(this: IExecuteFunctions, value: string) {
|
||||
if (uuidValidate(value)) return true;
|
||||
|
@ -916,6 +917,32 @@ export function extractPageId(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) {
|
||||
if (database.includes('?v=')) {
|
||||
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 { 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', () => {
|
||||
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,
|
||||
extractDatabaseId,
|
||||
extractDatabaseMentionRLC,
|
||||
extractPageId,
|
||||
getPageId,
|
||||
formatBlocks,
|
||||
formatTitle,
|
||||
mapFilters,
|
||||
|
@ -401,9 +401,8 @@ export class NotionV2 implements INodeType {
|
|||
if (operation === 'get') {
|
||||
for (let i = 0; i < itemsLength; i++) {
|
||||
try {
|
||||
const pageId = extractPageId(
|
||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
||||
);
|
||||
const pageId = getPageId.call(this, i);
|
||||
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
responseData = await notionApiRequest.call(this, 'GET', `/pages/${pageId}`);
|
||||
if (simple) {
|
||||
|
@ -526,9 +525,7 @@ export class NotionV2 implements INodeType {
|
|||
if (operation === 'update') {
|
||||
for (let i = 0; i < itemsLength; i++) {
|
||||
try {
|
||||
const pageId = extractPageId(
|
||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
||||
);
|
||||
const pageId = getPageId.call(this, i);
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
const properties = this.getNodeParameter(
|
||||
'propertiesUi.propertyValues',
|
||||
|
@ -635,9 +632,7 @@ export class NotionV2 implements INodeType {
|
|||
if (operation === 'archive') {
|
||||
for (let i = 0; i < itemsLength; i++) {
|
||||
try {
|
||||
const pageId = extractPageId(
|
||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
||||
);
|
||||
const pageId = getPageId.call(this, i);
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
responseData = await notionApiRequest.call(this, 'PATCH', `/pages/${pageId}`, {
|
||||
archived: true,
|
||||
|
@ -672,9 +667,7 @@ export class NotionV2 implements INodeType {
|
|||
parent: {},
|
||||
properties: {},
|
||||
};
|
||||
body.parent.page_id = extractPageId(
|
||||
this.getNodeParameter('pageId', i, '', { extractValue: true }) as string,
|
||||
);
|
||||
body.parent.page_id = getPageId.call(this, i);
|
||||
body.properties = formatTitle(this.getNodeParameter('title', i) as string);
|
||||
const blockValues = this.getNodeParameter(
|
||||
'blockUi.blockValues',
|
||||
|
|
Loading…
Reference in a new issue