mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Worked on validation and errors
This commit is contained in:
parent
5780596cb1
commit
81ce5c69d6
|
@ -79,7 +79,6 @@ export class MicrosoftCosmosDbSharedKeyApi implements ICredentialType {
|
||||||
|
|
||||||
const url = new URL(requestOptions.baseURL + requestOptions.url);
|
const url = new URL(requestOptions.baseURL + requestOptions.url);
|
||||||
const pathSegments = url.pathname.split('/').filter((segment) => segment);
|
const pathSegments = url.pathname.split('/').filter((segment) => segment);
|
||||||
console.log('Filtered Path Segments:', pathSegments);
|
|
||||||
|
|
||||||
let resourceType = '';
|
let resourceType = '';
|
||||||
let resourceId = '';
|
let resourceId = '';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
||||||
INodeListSearchItems,
|
INodeListSearchItems,
|
||||||
INodeListSearchResult,
|
INodeListSearchResult,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { ApplicationError } from 'n8n-workflow';
|
import { ApplicationError, NodeApiError } from 'n8n-workflow';
|
||||||
|
|
||||||
export const HeaderConstants = {
|
export const HeaderConstants = {
|
||||||
// Required
|
// Required
|
||||||
|
@ -123,9 +123,11 @@ export async function handleErrorPostReceive(
|
||||||
data: INodeExecutionData[],
|
data: INodeExecutionData[],
|
||||||
response: IN8nHttpFullResponse,
|
response: IN8nHttpFullResponse,
|
||||||
): Promise<INodeExecutionData[]> {
|
): Promise<INodeExecutionData[]> {
|
||||||
|
console.log('Status code❌', response.statusCode);
|
||||||
|
|
||||||
if (String(response.statusCode).startsWith('4') || String(response.statusCode).startsWith('5')) {
|
if (String(response.statusCode).startsWith('4') || String(response.statusCode).startsWith('5')) {
|
||||||
const responseBody = response.body as IDataObject;
|
const responseBody = response.body as IDataObject;
|
||||||
|
console.log('Got here ❌', responseBody);
|
||||||
let errorMessage = 'Unknown error occurred';
|
let errorMessage = 'Unknown error occurred';
|
||||||
|
|
||||||
if (typeof responseBody.message === 'string') {
|
if (typeof responseBody.message === 'string') {
|
||||||
|
@ -386,6 +388,7 @@ export async function validateOperations(
|
||||||
throw new ApplicationError('The "increment" operation must have a numeric value.');
|
throw new ApplicationError('The "increment" operation must have a numeric value.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//To-Do-check to not send properties it doesn't need
|
||||||
return {
|
return {
|
||||||
op: operation.op,
|
op: operation.op,
|
||||||
path: operation.op === 'move' ? operation.toPath?.value : operation.path?.value,
|
path: operation.op === 'move' ? operation.toPath?.value : operation.path?.value,
|
||||||
|
@ -640,3 +643,156 @@ export async function getDynamicFields(
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchPartitionKeyField(
|
||||||
|
this: ILoadOptionsFunctions,
|
||||||
|
): Promise<INodeListSearchResult> {
|
||||||
|
const collection = this.getNodeParameter('collId', '') as { mode: string; value: string };
|
||||||
|
|
||||||
|
if (!collection?.value) {
|
||||||
|
throw new ApplicationError('Collection ID is required to determine the partition key.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const opts: IHttpRequestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
url: `/colls/${collection.value}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const responseData: IDataObject = await microsoftCosmosDbRequest.call(this, opts);
|
||||||
|
|
||||||
|
const partitionKey = responseData.partitionKey as
|
||||||
|
| {
|
||||||
|
paths: string[];
|
||||||
|
kind: string;
|
||||||
|
version: number;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
const partitionKeyPaths = partitionKey?.paths ?? [];
|
||||||
|
|
||||||
|
if (partitionKeyPaths.length === 0) {
|
||||||
|
return { results: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const partitionKeyField = partitionKeyPaths[0].replace('/', '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
name: partitionKeyField,
|
||||||
|
value: partitionKeyField,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validatePartitionKey(
|
||||||
|
this: IExecuteSingleFunctions,
|
||||||
|
requestOptions: IHttpRequestOptions,
|
||||||
|
): Promise<IHttpRequestOptions> {
|
||||||
|
const operation = this.getNodeParameter('operation') as string;
|
||||||
|
const customProperties = this.getNodeParameter('customProperties', {}) as IDataObject;
|
||||||
|
|
||||||
|
const partitionKeyResult = await fetchPartitionKeyField.call(
|
||||||
|
this as unknown as ILoadOptionsFunctions,
|
||||||
|
);
|
||||||
|
const partitionKeyField =
|
||||||
|
partitionKeyResult.results.length > 0 ? partitionKeyResult.results[0].value : '';
|
||||||
|
|
||||||
|
if (!partitionKeyField) {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Partition key not found',
|
||||||
|
description: 'Failed to determine the partition key for this collection.',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(typeof partitionKeyField === 'string' || typeof partitionKeyField === 'number')) {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Invalid partition key',
|
||||||
|
description: `Partition key must be a string or number, but got ${typeof partitionKeyField}.`,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsedProperties: Record<string, unknown>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
parsedProperties =
|
||||||
|
typeof customProperties === 'string' ? JSON.parse(customProperties) : customProperties;
|
||||||
|
} catch (error) {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Invalid custom properties format',
|
||||||
|
description: 'Custom properties must be a valid JSON object.',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let id: string | undefined | { mode: string; value: string };
|
||||||
|
let partitionKeyValue: string | undefined;
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
if (partitionKeyField === 'id') {
|
||||||
|
partitionKeyValue = this.getNodeParameter('newId', '') as string;
|
||||||
|
} else {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(parsedProperties, partitionKeyField)) {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Partition key not found in custom properties',
|
||||||
|
description: `Partition key "${partitionKeyField}" must be present and have a valid, non-empty value in custom properties.`,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
partitionKeyValue = parsedProperties[partitionKeyField] as string;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (partitionKeyField === 'id') {
|
||||||
|
id = this.getNodeParameter('id', {}) as { mode: string; value: string };
|
||||||
|
|
||||||
|
if (!id?.value) {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Item ID is missing or invalid',
|
||||||
|
description: "The item must have a valid value selected from 'Item'",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
partitionKeyValue = id.value;
|
||||||
|
} else {
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', {}) as IDataObject;
|
||||||
|
partitionKeyValue = additionalFields.partitionKey as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partitionKeyValue === undefined || partitionKeyValue === null || partitionKeyValue === '') {
|
||||||
|
throw new NodeApiError(
|
||||||
|
this.getNode(),
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
message: 'Partition key value is missing or empty',
|
||||||
|
description: `Provide a value for partition key "${partitionKeyField}" in "Partition Key" field.`,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestOptions.headers = {
|
||||||
|
...requestOptions.headers,
|
||||||
|
'x-ms-documentdb-partitionkey': `["${partitionKeyValue}"]`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return requestOptions;
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
handlePagination,
|
handlePagination,
|
||||||
processResponseItems,
|
processResponseItems,
|
||||||
validateOperations,
|
validateOperations,
|
||||||
|
validatePartitionKey,
|
||||||
validateQueryParameters,
|
validateQueryParameters,
|
||||||
} from '../GenericFunctions';
|
} from '../GenericFunctions';
|
||||||
|
|
||||||
|
@ -27,18 +28,18 @@ export const itemOperations: INodeProperties[] = [
|
||||||
description: 'Create a new item',
|
description: 'Create a new item',
|
||||||
routing: {
|
routing: {
|
||||||
send: {
|
send: {
|
||||||
preSend: [formatCustomProperties],
|
preSend: [formatCustomProperties, validatePartitionKey],
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||||
//To-Do-do it based on the partition key of collection and only one
|
|
||||||
headers: {
|
headers: {
|
||||||
'x-ms-documentdb-partitionkey': '=["{{$parameter["newId"]}}"]',
|
|
||||||
'x-ms-documentdb-is-upsert': 'True',
|
'x-ms-documentdb-is-upsert': 'True',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
postReceive: [handleErrorPostReceive],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
action: 'Create item',
|
action: 'Create item',
|
||||||
},
|
},
|
||||||
|
@ -47,17 +48,16 @@ export const itemOperations: INodeProperties[] = [
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
description: 'Delete an existing item',
|
description: 'Delete an existing item',
|
||||||
routing: {
|
routing: {
|
||||||
|
send: {
|
||||||
|
preSend: [validatePartitionKey],
|
||||||
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||||
//To-Do-do it based on the partition key of collection and only one
|
|
||||||
headers: {
|
|
||||||
'x-ms-documentdb-partitionkey': '=["{{$parameter["id"]}}"]',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
postReceive: [
|
postReceive: [
|
||||||
|
handleErrorPostReceive,
|
||||||
{
|
{
|
||||||
type: 'set',
|
type: 'set',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -74,16 +74,19 @@ export const itemOperations: INodeProperties[] = [
|
||||||
value: 'get',
|
value: 'get',
|
||||||
description: 'Retrieve an item',
|
description: 'Retrieve an item',
|
||||||
routing: {
|
routing: {
|
||||||
|
send: {
|
||||||
|
preSend: [validatePartitionKey],
|
||||||
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '=/colls/{{ $parameter["collId"]}}/docs/{{$parameter["id"]}}',
|
url: '=/colls/{{ $parameter["collId"]}}/docs/{{$parameter["id"]}}',
|
||||||
headers: {
|
headers: {
|
||||||
//To-Do-do it based on the partition key of collection and only one
|
|
||||||
'x-ms-documentdb-partitionkey': '=["{{$parameter["id"]}}"]',
|
|
||||||
'x-ms-documentdb-is-upsert': 'True',
|
'x-ms-documentdb-is-upsert': 'True',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
postReceive: [handleErrorPostReceive],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
action: 'Get item',
|
action: 'Get item',
|
||||||
},
|
},
|
||||||
|
@ -99,12 +102,11 @@ export const itemOperations: INodeProperties[] = [
|
||||||
pagination: handlePagination,
|
pagination: handlePagination,
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
postReceive: [processResponseItems],
|
postReceive: [processResponseItems, handleErrorPostReceive],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action: 'Get many items',
|
action: 'Get many items',
|
||||||
|
@ -118,7 +120,6 @@ export const itemOperations: INodeProperties[] = [
|
||||||
preSend: [validateQueryParameters],
|
preSend: [validateQueryParameters],
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
url: '=/colls/{{ $parameter["collId"] }}/docs',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -126,6 +127,9 @@ export const itemOperations: INodeProperties[] = [
|
||||||
'x-ms-documentdb-isquery': 'True',
|
'x-ms-documentdb-isquery': 'True',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
output: {
|
||||||
|
postReceive: [handleErrorPostReceive],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
action: 'Query items',
|
action: 'Query items',
|
||||||
},
|
},
|
||||||
|
@ -135,15 +139,13 @@ export const itemOperations: INodeProperties[] = [
|
||||||
description: 'Update an existing item',
|
description: 'Update an existing item',
|
||||||
routing: {
|
routing: {
|
||||||
send: {
|
send: {
|
||||||
preSend: [validateOperations],
|
preSend: [validateOperations, validatePartitionKey],
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
ignoreHttpStatusErrors: true,
|
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
url: '=/colls/{{ $parameter["collId"] }}/docs/{{ $parameter["id"] }}',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json-patch+json',
|
'Content-Type': 'application/json-patch+json',
|
||||||
'x-ms-documentdb-partitionkey': '=["{{$parameter["id"]}}"]',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
@ -330,6 +332,28 @@ export const deleteFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Partition Key',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['item'],
|
||||||
|
operation: ['delete'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Partition Key',
|
||||||
|
name: 'partitionKey',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Specify the partition key for this item',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getFields: INodeProperties[] = [
|
export const getFields: INodeProperties[] = [
|
||||||
|
@ -421,6 +445,28 @@ export const getFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Partition Key',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['item'],
|
||||||
|
operation: ['get'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Partition Key',
|
||||||
|
name: 'partitionKey',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Specify the partition key for this item',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getAllFields: INodeProperties[] = [
|
export const getAllFields: INodeProperties[] = [
|
||||||
|
@ -862,6 +908,28 @@ export const updateFields: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Partition Key',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: ['item'],
|
||||||
|
operation: ['update'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Partition Key',
|
||||||
|
name: 'partitionKey',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'Specify the partition key for this item',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const itemFields: INodeProperties[] = [
|
export const itemFields: INodeProperties[] = [
|
||||||
|
|
Loading…
Reference in a new issue