mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
fix: Update operations to run per item (#8967)
Co-authored-by: Elias Meire <elias@meire.dev>
This commit is contained in:
parent
870412f093
commit
ef9d4aba90
|
@ -1,4 +1,4 @@
|
||||||
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
|
import type { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
||||||
|
|
||||||
export class CodaApi implements ICredentialType {
|
export class CodaApi implements ICredentialType {
|
||||||
name = 'codaApi';
|
name = 'codaApi';
|
||||||
|
@ -16,4 +16,13 @@ export class CodaApi implements ICredentialType {
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
test: ICredentialTestRequest = {
|
||||||
|
request: {
|
||||||
|
baseURL: 'https://coda.io/apis/v1/whoami',
|
||||||
|
headers: {
|
||||||
|
Authorization: '=Bearer {{$credentials.accessToken}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,13 @@ export class Airtable extends VersionedNodeType {
|
||||||
icon: 'file:airtable.svg',
|
icon: 'file:airtable.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
description: 'Read, update, write and delete data from Airtable',
|
description: 'Read, update, write and delete data from Airtable',
|
||||||
defaultVersion: 2,
|
defaultVersion: 2.1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||||
1: new AirtableV1(baseDescription),
|
1: new AirtableV1(baseDescription),
|
||||||
2: new AirtableV2(baseDescription),
|
2: new AirtableV2(baseDescription),
|
||||||
|
2.1: new AirtableV2(baseDescription),
|
||||||
};
|
};
|
||||||
|
|
||||||
super(nodeVersions, baseDescription);
|
super(nodeVersions, baseDescription);
|
||||||
|
|
|
@ -149,74 +149,90 @@ export async function execute(
|
||||||
base: string,
|
base: string,
|
||||||
table: string,
|
table: string,
|
||||||
): Promise<INodeExecutionData[]> {
|
): Promise<INodeExecutionData[]> {
|
||||||
let returnData: INodeExecutionData[] = [];
|
const returnData: INodeExecutionData[] = [];
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
const body: IDataObject = {};
|
|
||||||
const qs: IDataObject = {};
|
|
||||||
|
|
||||||
const endpoint = `${base}/${table}`;
|
const endpoint = `${base}/${table}`;
|
||||||
|
|
||||||
try {
|
let itemsLength = items.length ? 1 : 0;
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
let fallbackPairedItems;
|
||||||
const options = this.getNodeParameter('options', 0, {});
|
|
||||||
const sort = this.getNodeParameter('sort', 0, {}) as IDataObject;
|
|
||||||
const filterByFormula = this.getNodeParameter('filterByFormula', 0) as string;
|
|
||||||
|
|
||||||
if (filterByFormula) {
|
if (nodeVersion >= 2.1) {
|
||||||
qs.filterByFormula = filterByFormula;
|
itemsLength = items.length;
|
||||||
}
|
} else {
|
||||||
|
fallbackPairedItems = generatePairedItemData(items.length);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.fields) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
if (typeof options.fields === 'string') {
|
try {
|
||||||
qs.fields = options.fields.split(',').map((field) => field.trim());
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
} else {
|
const options = this.getNodeParameter('options', i, {});
|
||||||
qs.fields = options.fields as string[];
|
const sort = this.getNodeParameter('sort', i, {}) as IDataObject;
|
||||||
|
const filterByFormula = this.getNodeParameter('filterByFormula', i) as string;
|
||||||
|
|
||||||
|
const body: IDataObject = {};
|
||||||
|
const qs: IDataObject = {};
|
||||||
|
|
||||||
|
if (filterByFormula) {
|
||||||
|
qs.filterByFormula = filterByFormula;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (sort.property) {
|
if (options.fields) {
|
||||||
qs.sort = sort.property;
|
if (typeof options.fields === 'string') {
|
||||||
}
|
qs.fields = options.fields.split(',').map((field) => field.trim());
|
||||||
|
} else {
|
||||||
|
qs.fields = options.fields as string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.view) {
|
if (sort.property) {
|
||||||
qs.view = (options.view as IDataObject).value as string;
|
qs.sort = sort.property;
|
||||||
}
|
}
|
||||||
|
|
||||||
let responseData;
|
if (options.view) {
|
||||||
|
qs.view = (options.view as IDataObject).value as string;
|
||||||
|
}
|
||||||
|
|
||||||
if (returnAll) {
|
let responseData;
|
||||||
responseData = await apiRequestAllItems.call(this, 'GET', endpoint, body, qs);
|
|
||||||
} else {
|
|
||||||
qs.maxRecords = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await apiRequest.call(this, 'GET', endpoint, body, qs);
|
|
||||||
}
|
|
||||||
|
|
||||||
returnData = responseData.records as INodeExecutionData[];
|
if (returnAll) {
|
||||||
|
responseData = await apiRequestAllItems.call(this, 'GET', endpoint, body, qs);
|
||||||
|
} else {
|
||||||
|
qs.maxRecords = this.getNodeParameter('limit', i);
|
||||||
|
responseData = await apiRequest.call(this, 'GET', endpoint, body, qs);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.downloadFields) {
|
if (options.downloadFields) {
|
||||||
const pairedItem = generatePairedItemData(items.length);
|
const itemWithAttachments = await downloadRecordAttachments.call(
|
||||||
return await downloadRecordAttachments.call(
|
this,
|
||||||
this,
|
responseData.records as IRecord[],
|
||||||
responseData.records as IRecord[],
|
options.downloadFields as string[],
|
||||||
options.downloadFields as string[],
|
fallbackPairedItems || [{ item: i }],
|
||||||
pairedItem,
|
);
|
||||||
);
|
returnData.push(...itemWithAttachments);
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
returnData = returnData.map((record) => ({
|
let records = responseData.records;
|
||||||
json: flattenOutput(record as IDataObject),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const itemData = generatePairedItemData(items.length);
|
records = (records as IDataObject[]).map((record) => ({
|
||||||
|
json: flattenOutput(record),
|
||||||
|
})) as INodeExecutionData[];
|
||||||
|
|
||||||
returnData = this.helpers.constructExecutionMetaData(returnData, {
|
const itemData = fallbackPairedItems || [{ item: i }];
|
||||||
itemData,
|
|
||||||
});
|
const executionData = this.helpers.constructExecutionMetaData(records, {
|
||||||
} catch (error) {
|
itemData,
|
||||||
if (this.continueOnFail()) {
|
});
|
||||||
returnData.push({ json: { message: error.message, error } });
|
|
||||||
} else {
|
returnData.push(...executionData);
|
||||||
throw error;
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({ json: { message: error.message, error }, pairedItem: { item: i } });
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const versionDescription: INodeTypeDescription = {
|
||||||
name: 'airtable',
|
name: 'airtable',
|
||||||
icon: 'file:airtable.svg',
|
icon: 'file:airtable.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 2,
|
version: [2, 2.1],
|
||||||
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
|
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
|
||||||
description: 'Read, update, write and delete data from Airtable',
|
description: 'Read, update, write and delete data from Airtable',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class Coda implements INodeType {
|
||||||
name: 'coda',
|
name: 'coda',
|
||||||
icon: 'file:coda.svg',
|
icon: 'file:coda.svg',
|
||||||
group: ['output'],
|
group: ['output'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
description: 'Consume Coda API',
|
description: 'Consume Coda API',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
@ -240,6 +240,7 @@ export class Coda implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
const returnData: INodeExecutionData[] = [];
|
const returnData: INodeExecutionData[] = [];
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
let responseData;
|
let responseData;
|
||||||
|
@ -363,61 +364,83 @@ export class Coda implements INodeType {
|
||||||
}
|
}
|
||||||
// https://coda.io/developers/apis/v1beta1#operation/listRows
|
// https://coda.io/developers/apis/v1beta1#operation/listRows
|
||||||
if (operation === 'getAllRows') {
|
if (operation === 'getAllRows') {
|
||||||
const docId = this.getNodeParameter('docId', 0) as string;
|
let itemsLength = items.length ? 1 : 0;
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
|
||||||
const tableId = this.getNodeParameter('tableId', 0) as string;
|
if (nodeVersion >= 1.1) {
|
||||||
const options = this.getNodeParameter('options', 0);
|
itemsLength = items.length;
|
||||||
const endpoint = `/docs/${docId}/tables/${tableId}/rows`;
|
|
||||||
if (options.useColumnNames === false) {
|
|
||||||
qs.useColumnNames = options.useColumnNames as boolean;
|
|
||||||
} else {
|
|
||||||
qs.useColumnNames = true;
|
|
||||||
}
|
|
||||||
if (options.valueFormat) {
|
|
||||||
qs.valueFormat = options.valueFormat as string;
|
|
||||||
}
|
|
||||||
if (options.sortBy) {
|
|
||||||
qs.sortBy = options.sortBy as string;
|
|
||||||
}
|
|
||||||
if (options.visibleOnly) {
|
|
||||||
qs.visibleOnly = options.visibleOnly as boolean;
|
|
||||||
}
|
|
||||||
if (options.query) {
|
|
||||||
qs.query = options.query as string;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (returnAll) {
|
|
||||||
responseData = await codaApiRequestAllItems.call(
|
|
||||||
this,
|
|
||||||
'items',
|
|
||||||
'GET',
|
|
||||||
endpoint,
|
|
||||||
{},
|
|
||||||
qs,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
qs.limit = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
|
||||||
responseData = responseData.items;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (this.continueOnFail()) {
|
|
||||||
return [this.helpers.returnJsonArray({ error: error.message })];
|
|
||||||
}
|
|
||||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.rawData === true) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
return [this.helpers.returnJsonArray(responseData as IDataObject[])];
|
const docId = this.getNodeParameter('docId', i) as string;
|
||||||
} else {
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
for (const item of responseData) {
|
const tableId = this.getNodeParameter('tableId', i) as string;
|
||||||
returnData.push({
|
const options = this.getNodeParameter('options', i);
|
||||||
id: item.id,
|
const endpoint = `/docs/${docId}/tables/${tableId}/rows`;
|
||||||
...item.values,
|
if (options.useColumnNames === false) {
|
||||||
});
|
qs.useColumnNames = options.useColumnNames as boolean;
|
||||||
|
} else {
|
||||||
|
qs.useColumnNames = true;
|
||||||
|
}
|
||||||
|
if (options.valueFormat) {
|
||||||
|
qs.valueFormat = options.valueFormat as string;
|
||||||
|
}
|
||||||
|
if (options.sortBy) {
|
||||||
|
qs.sortBy = options.sortBy as string;
|
||||||
|
}
|
||||||
|
if (options.visibleOnly) {
|
||||||
|
qs.visibleOnly = options.visibleOnly as boolean;
|
||||||
|
}
|
||||||
|
if (options.query) {
|
||||||
|
qs.query = options.query as string;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await codaApiRequestAllItems.call(
|
||||||
|
this,
|
||||||
|
'items',
|
||||||
|
'GET',
|
||||||
|
endpoint,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
qs.limit = this.getNodeParameter('limit', i);
|
||||||
|
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
|
responseData = responseData.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.rawData === true) {
|
||||||
|
for (const item of responseData) {
|
||||||
|
returnData.push({
|
||||||
|
json: item,
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const item of responseData) {
|
||||||
|
returnData.push({
|
||||||
|
json: {
|
||||||
|
id: item.id,
|
||||||
|
...item.values,
|
||||||
|
},
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (error instanceof NodeApiError) throw error;
|
||||||
|
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||||
}
|
}
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [returnData];
|
||||||
}
|
}
|
||||||
// https://coda.io/developers/apis/v1beta1#operation/deleteRows
|
// https://coda.io/developers/apis/v1beta1#operation/deleteRows
|
||||||
if (operation === 'deleteRow') {
|
if (operation === 'deleteRow') {
|
||||||
|
@ -630,15 +653,15 @@ export class Coda implements INodeType {
|
||||||
}
|
}
|
||||||
//https://coda.io/developers/apis/v1beta1#operation/listControls
|
//https://coda.io/developers/apis/v1beta1#operation/listControls
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0);
|
||||||
|
qs.limit = this.getNodeParameter('limit', 0);
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
try {
|
try {
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
|
||||||
const docId = this.getNodeParameter('docId', i) as string;
|
const docId = this.getNodeParameter('docId', i) as string;
|
||||||
const endpoint = `/docs/${docId}/controls`;
|
const endpoint = `/docs/${docId}/controls`;
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
||||||
} else {
|
} else {
|
||||||
qs.limit = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
responseData = responseData.items;
|
responseData = responseData.items;
|
||||||
}
|
}
|
||||||
|
@ -680,15 +703,15 @@ export class Coda implements INodeType {
|
||||||
}
|
}
|
||||||
//https://coda.io/developers/apis/v1beta1#operation/listViews
|
//https://coda.io/developers/apis/v1beta1#operation/listViews
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0);
|
||||||
|
qs.limit = this.getNodeParameter('limit', 0);
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
try {
|
try {
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
|
||||||
const docId = this.getNodeParameter('docId', i) as string;
|
const docId = this.getNodeParameter('docId', i) as string;
|
||||||
const endpoint = `/docs/${docId}/tables?tableTypes=view`;
|
const endpoint = `/docs/${docId}/tables?tableTypes=view`;
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
||||||
} else {
|
} else {
|
||||||
qs.limit = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
responseData = responseData.items;
|
responseData = responseData.items;
|
||||||
}
|
}
|
||||||
|
@ -712,58 +735,80 @@ export class Coda implements INodeType {
|
||||||
return [returnData];
|
return [returnData];
|
||||||
}
|
}
|
||||||
if (operation === 'getAllViewRows') {
|
if (operation === 'getAllViewRows') {
|
||||||
const docId = this.getNodeParameter('docId', 0) as string;
|
let itemsLength = items.length ? 1 : 0;
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
|
||||||
const viewId = this.getNodeParameter('viewId', 0) as string;
|
if (nodeVersion >= 1.1) {
|
||||||
const options = this.getNodeParameter('options', 0);
|
itemsLength = items.length;
|
||||||
const endpoint = `/docs/${docId}/tables/${viewId}/rows`;
|
|
||||||
if (options.useColumnNames === false) {
|
|
||||||
qs.useColumnNames = options.useColumnNames as boolean;
|
|
||||||
} else {
|
|
||||||
qs.useColumnNames = true;
|
|
||||||
}
|
|
||||||
if (options.valueFormat) {
|
|
||||||
qs.valueFormat = options.valueFormat as string;
|
|
||||||
}
|
|
||||||
if (options.sortBy) {
|
|
||||||
qs.sortBy = options.sortBy as string;
|
|
||||||
}
|
|
||||||
if (options.query) {
|
|
||||||
qs.query = options.query as string;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (returnAll) {
|
|
||||||
responseData = await codaApiRequestAllItems.call(
|
|
||||||
this,
|
|
||||||
'items',
|
|
||||||
'GET',
|
|
||||||
endpoint,
|
|
||||||
{},
|
|
||||||
qs,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
qs.limit = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
|
||||||
responseData = responseData.items;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (this.continueOnFail()) {
|
|
||||||
return [this.helpers.returnJsonArray({ error: error.message })];
|
|
||||||
}
|
|
||||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.rawData === true) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
return [this.helpers.returnJsonArray(responseData as IDataObject[])];
|
const docId = this.getNodeParameter('docId', i) as string;
|
||||||
} else {
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
for (const item of responseData) {
|
const viewId = this.getNodeParameter('viewId', i) as string;
|
||||||
returnData.push({
|
const options = this.getNodeParameter('options', i);
|
||||||
id: item.id,
|
const endpoint = `/docs/${docId}/tables/${viewId}/rows`;
|
||||||
...item.values,
|
if (options.useColumnNames === false) {
|
||||||
});
|
qs.useColumnNames = options.useColumnNames as boolean;
|
||||||
|
} else {
|
||||||
|
qs.useColumnNames = true;
|
||||||
|
}
|
||||||
|
if (options.valueFormat) {
|
||||||
|
qs.valueFormat = options.valueFormat as string;
|
||||||
|
}
|
||||||
|
if (options.sortBy) {
|
||||||
|
qs.sortBy = options.sortBy as string;
|
||||||
|
}
|
||||||
|
if (options.query) {
|
||||||
|
qs.query = options.query as string;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await codaApiRequestAllItems.call(
|
||||||
|
this,
|
||||||
|
'items',
|
||||||
|
'GET',
|
||||||
|
endpoint,
|
||||||
|
{},
|
||||||
|
qs,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
qs.limit = this.getNodeParameter('limit', i);
|
||||||
|
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
|
responseData = responseData.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.rawData === true) {
|
||||||
|
for (const item of responseData) {
|
||||||
|
returnData.push({
|
||||||
|
json: item,
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const item of responseData) {
|
||||||
|
returnData.push({
|
||||||
|
json: {
|
||||||
|
id: item.id,
|
||||||
|
...item.values,
|
||||||
|
},
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (error instanceof NodeApiError) throw error;
|
||||||
|
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||||
}
|
}
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [returnData];
|
||||||
}
|
}
|
||||||
//https://coda.io/developers/apis/v1beta1#operation/deleteViewRow
|
//https://coda.io/developers/apis/v1beta1#operation/deleteViewRow
|
||||||
if (operation === 'deleteViewRow') {
|
if (operation === 'deleteViewRow') {
|
||||||
|
@ -823,16 +868,16 @@ export class Coda implements INodeType {
|
||||||
return [returnData];
|
return [returnData];
|
||||||
}
|
}
|
||||||
if (operation === 'getAllViewColumns') {
|
if (operation === 'getAllViewColumns') {
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0);
|
||||||
|
qs.limit = this.getNodeParameter('limit', 0);
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
try {
|
try {
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
|
||||||
const docId = this.getNodeParameter('docId', i) as string;
|
const docId = this.getNodeParameter('docId', i) as string;
|
||||||
const viewId = this.getNodeParameter('viewId', i) as string;
|
const viewId = this.getNodeParameter('viewId', i) as string;
|
||||||
const endpoint = `/docs/${docId}/tables/${viewId}/columns`;
|
const endpoint = `/docs/${docId}/tables/${viewId}/columns`;
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
responseData = await codaApiRequestAllItems.call(this, 'items', 'GET', endpoint, {});
|
||||||
} else {
|
} else {
|
||||||
qs.limit = this.getNodeParameter('limit', 0);
|
|
||||||
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
responseData = await codaApiRequest.call(this, 'GET', endpoint, {}, qs);
|
||||||
responseData = responseData.items;
|
responseData = responseData.items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,13 @@ export const tableOperations: INodeProperties[] = [
|
||||||
{
|
{
|
||||||
name: 'Get All Columns',
|
name: 'Get All Columns',
|
||||||
value: 'getAllColumns',
|
value: 'getAllColumns',
|
||||||
|
description: 'Get all columns in a table',
|
||||||
action: 'Get all columns',
|
action: 'Get all columns',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Get All Rows',
|
name: 'Get All Rows',
|
||||||
value: 'getAllRows',
|
value: 'getAllRows',
|
||||||
description: 'Get all the rows',
|
description: 'Get all rows in a table',
|
||||||
action: 'Get all rows',
|
action: 'Get all rows',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class GoogleFirebaseCloudFirestore implements INodeType {
|
||||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
||||||
icon: 'file:googleFirebaseCloudFirestore.png',
|
icon: 'file:googleFirebaseCloudFirestore.png',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||||
description: 'Interact with Google Firebase - Cloud Firestore API',
|
description: 'Interact with Google Firebase - Cloud Firestore API',
|
||||||
defaults: {
|
defaults: {
|
||||||
|
@ -94,15 +94,27 @@ export class GoogleFirebaseCloudFirestore implements INodeType {
|
||||||
const itemData = generatePairedItemData(items.length);
|
const itemData = generatePairedItemData(items.length);
|
||||||
const returnData: INodeExecutionData[] = [];
|
const returnData: INodeExecutionData[] = [];
|
||||||
let responseData;
|
let responseData;
|
||||||
|
|
||||||
const resource = this.getNodeParameter('resource', 0);
|
const resource = this.getNodeParameter('resource', 0);
|
||||||
const operation = this.getNodeParameter('operation', 0);
|
const operation = this.getNodeParameter('operation', 0);
|
||||||
|
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
let itemsLength = items.length ? 1 : 0;
|
||||||
|
let fallbackPairedItems;
|
||||||
|
|
||||||
|
if (nodeVersion >= 1.1) {
|
||||||
|
itemsLength = items.length;
|
||||||
|
} else {
|
||||||
|
fallbackPairedItems = generatePairedItemData(items.length);
|
||||||
|
}
|
||||||
|
|
||||||
if (resource === 'document') {
|
if (resource === 'document') {
|
||||||
if (operation === 'get') {
|
if (operation === 'get') {
|
||||||
const projectId = this.getNodeParameter('projectId', 0) as string;
|
const projectId = this.getNodeParameter('projectId', 0) as string;
|
||||||
const database = this.getNodeParameter('database', 0) as string;
|
const database = this.getNodeParameter('database', 0) as string;
|
||||||
const simple = this.getNodeParameter('simple', 0) as boolean;
|
const simple = this.getNodeParameter('simple', 0) as boolean;
|
||||||
const documentList = items.map((item: IDataObject, i: number) => {
|
const documentList = items.map((_: IDataObject, i: number) => {
|
||||||
const collection = this.getNodeParameter('collection', i) as string;
|
const collection = this.getNodeParameter('collection', i) as string;
|
||||||
const documentId = this.getNodeParameter('documentId', i) as string;
|
const documentId = this.getNodeParameter('documentId', i) as string;
|
||||||
return `projects/${projectId}/databases/${database}/documents/${collection}/${documentId}`;
|
return `projects/${projectId}/databases/${database}/documents/${collection}/${documentId}`;
|
||||||
|
@ -179,49 +191,64 @@ export class GoogleFirebaseCloudFirestore implements INodeType {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else if (operation === 'getAll') {
|
} else if (operation === 'getAll') {
|
||||||
const projectId = this.getNodeParameter('projectId', 0) as string;
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
const database = this.getNodeParameter('database', 0) as string;
|
try {
|
||||||
const collection = this.getNodeParameter('collection', 0) as string;
|
const projectId = this.getNodeParameter('projectId', i) as string;
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
const database = this.getNodeParameter('database', i) as string;
|
||||||
const simple = this.getNodeParameter('simple', 0) as boolean;
|
const collection = this.getNodeParameter('collection', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||||
|
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
responseData = await googleApiRequestAllItems.call(
|
responseData = await googleApiRequestAllItems.call(
|
||||||
this,
|
this,
|
||||||
'documents',
|
'documents',
|
||||||
'GET',
|
'GET',
|
||||||
`/${projectId}/databases/${database}/documents/${collection}`,
|
`/${projectId}/databases/${database}/documents/${collection}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const limit = this.getNodeParameter('limit', 0);
|
const limit = this.getNodeParameter('limit', i);
|
||||||
const getAllResponse = (await googleApiRequest.call(
|
const getAllResponse = (await googleApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'GET',
|
'GET',
|
||||||
`/${projectId}/databases/${database}/documents/${collection}`,
|
`/${projectId}/databases/${database}/documents/${collection}`,
|
||||||
{},
|
{},
|
||||||
{ pageSize: limit },
|
{ pageSize: limit },
|
||||||
)) as IDataObject;
|
)) as IDataObject;
|
||||||
responseData = getAllResponse.documents;
|
responseData = getAllResponse.documents;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseData = responseData.map((element: IDataObject) => {
|
||||||
|
element.id = (element.name as string).split('/').pop();
|
||||||
|
return element;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (simple) {
|
||||||
|
responseData = responseData.map((element: IDataObject) =>
|
||||||
|
fullDocumentToJson(element),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
|
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||||
|
{ itemData: fallbackPairedItems ?? [{ item: i }] },
|
||||||
|
);
|
||||||
|
|
||||||
|
returnData.push(...executionData);
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = responseData.map((element: IDataObject) => {
|
|
||||||
element.id = (element.name as string).split('/').pop();
|
|
||||||
return element;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (simple) {
|
|
||||||
responseData = responseData.map((element: IDataObject) => fullDocumentToJson(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
|
||||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
|
||||||
{ itemData },
|
|
||||||
);
|
|
||||||
|
|
||||||
returnData.push(...executionData);
|
|
||||||
} else if (operation === 'delete') {
|
} else if (operation === 'delete') {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
items.map(async (item: IDataObject, i: number) => {
|
items.map(async (_: IDataObject, i: number) => {
|
||||||
const projectId = this.getNodeParameter('projectId', i) as string;
|
const projectId = this.getNodeParameter('projectId', i) as string;
|
||||||
const database = this.getNodeParameter('database', i) as string;
|
const database = this.getNodeParameter('database', i) as string;
|
||||||
const collection = this.getNodeParameter('collection', i) as string;
|
const collection = this.getNodeParameter('collection', i) as string;
|
||||||
|
@ -295,44 +322,13 @@ export class GoogleFirebaseCloudFirestore implements INodeType {
|
||||||
|
|
||||||
returnData.push(...executionData);
|
returnData.push(...executionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// } else if (operation === 'update') {
|
|
||||||
// const projectId = this.getNodeParameter('projectId', 0) as string;
|
|
||||||
// const database = this.getNodeParameter('database', 0) as string;
|
|
||||||
// const simple = this.getNodeParameter('simple', 0) as boolean;
|
|
||||||
|
|
||||||
// await Promise.all(items.map(async (item: IDataObject, i: number) => {
|
|
||||||
// const collection = this.getNodeParameter('collection', i) as string;
|
|
||||||
// const updateKey = this.getNodeParameter('updateKey', i) as string;
|
|
||||||
// // @ts-ignore
|
|
||||||
// const documentId = item['json'][updateKey] as string;
|
|
||||||
// const columns = this.getNodeParameter('columns', i) as string;
|
|
||||||
// const columnList = columns.split(',').map(column => column.trim()) as string[];
|
|
||||||
// const document = {};
|
|
||||||
// columnList.map(column => {
|
|
||||||
// // @ts-ignore
|
|
||||||
// document[column] = item['json'].hasOwnProperty(column) ? jsonToDocument(item['json'][column]) : jsonToDocument(null);
|
|
||||||
// });
|
|
||||||
// responseData = await googleApiRequest.call(
|
|
||||||
// this,
|
|
||||||
// 'PATCH',
|
|
||||||
// `/${projectId}/databases/${database}/documents/${collection}/${documentId}`,
|
|
||||||
// { fields: document },
|
|
||||||
// { [`updateMask.fieldPaths`]: columnList },
|
|
||||||
// );
|
|
||||||
// if (simple === false) {
|
|
||||||
// returnData.push(responseData);
|
|
||||||
// } else {
|
|
||||||
// returnData.push(fullDocumentToJson(responseData as IDataObject));
|
|
||||||
// }
|
|
||||||
// }));
|
|
||||||
} else if (operation === 'query') {
|
} else if (operation === 'query') {
|
||||||
const projectId = this.getNodeParameter('projectId', 0) as string;
|
const projectId = this.getNodeParameter('projectId', 0) as string;
|
||||||
const database = this.getNodeParameter('database', 0) as string;
|
const database = this.getNodeParameter('database', 0) as string;
|
||||||
const simple = this.getNodeParameter('simple', 0) as boolean;
|
const simple = this.getNodeParameter('simple', 0) as boolean;
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
items.map(async (item: IDataObject, i: number) => {
|
items.map(async (_: IDataObject, i: number) => {
|
||||||
const query = this.getNodeParameter('query', i) as string;
|
const query = this.getNodeParameter('query', i) as string;
|
||||||
responseData = await googleApiRequest.call(
|
responseData = await googleApiRequest.call(
|
||||||
this,
|
this,
|
||||||
|
@ -369,38 +365,51 @@ export class GoogleFirebaseCloudFirestore implements INodeType {
|
||||||
}
|
}
|
||||||
} else if (resource === 'collection') {
|
} else if (resource === 'collection') {
|
||||||
if (operation === 'getAll') {
|
if (operation === 'getAll') {
|
||||||
const projectId = this.getNodeParameter('projectId', 0) as string;
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
const database = this.getNodeParameter('database', 0) as string;
|
try {
|
||||||
const returnAll = this.getNodeParameter('returnAll', 0);
|
const projectId = this.getNodeParameter('projectId', i) as string;
|
||||||
|
const database = this.getNodeParameter('database', i) as string;
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', i);
|
||||||
|
|
||||||
if (returnAll) {
|
if (returnAll) {
|
||||||
const getAllResponse = await googleApiRequestAllItems.call(
|
const getAllResponse = await googleApiRequestAllItems.call(
|
||||||
this,
|
this,
|
||||||
'collectionIds',
|
'collectionIds',
|
||||||
'POST',
|
'POST',
|
||||||
`/${projectId}/databases/${database}/documents:listCollectionIds`,
|
`/${projectId}/databases/${database}/documents:listCollectionIds`,
|
||||||
);
|
);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
responseData = getAllResponse.map((o) => ({ name: o }));
|
responseData = getAllResponse.map((o) => ({ name: o }));
|
||||||
} else {
|
} else {
|
||||||
const limit = this.getNodeParameter('limit', 0);
|
const limit = this.getNodeParameter('limit', i);
|
||||||
const getAllResponse = (await googleApiRequest.call(
|
const getAllResponse = (await googleApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
`/${projectId}/databases/${database}/documents:listCollectionIds`,
|
`/${projectId}/databases/${database}/documents:listCollectionIds`,
|
||||||
{},
|
{},
|
||||||
{ pageSize: limit },
|
{ pageSize: limit },
|
||||||
)) as IDataObject;
|
)) as IDataObject;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
responseData = getAllResponse.collectionIds.map((o) => ({ name: o }));
|
responseData = getAllResponse.collectionIds.map((o) => ({ name: o }));
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
|
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||||
|
{ itemData: fallbackPairedItems ?? [{ item: i }] },
|
||||||
|
);
|
||||||
|
|
||||||
|
returnData.push(...executionData);
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
|
||||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
|
||||||
{ itemData },
|
|
||||||
);
|
|
||||||
|
|
||||||
returnData.push(...executionData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,6 @@ export async function execute(this: IExecuteFunctions): Promise<INodeExecutionDa
|
||||||
const returnData: INodeExecutionData[] = [];
|
const returnData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
// const spreadsheetId = this.getNodeParameter('spreadsheetId', i) as string;
|
|
||||||
const documentId = this.getNodeParameter('documentId', i, undefined, {
|
const documentId = this.getNodeParameter('documentId', i, undefined, {
|
||||||
extractValue: true,
|
extractValue: true,
|
||||||
}) as string;
|
}) as string;
|
||||||
|
|
|
@ -10,7 +10,6 @@ import type {
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeOperationError } from 'n8n-workflow';
|
|
||||||
|
|
||||||
import type { ITables } from './interfaces';
|
import type { ITables } from './interfaces';
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ export class MicrosoftSql implements INodeType {
|
||||||
name: 'microsoftSql',
|
name: 'microsoftSql',
|
||||||
icon: 'file:mssql.svg',
|
icon: 'file:mssql.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
description: 'Get, add and update data in Microsoft SQL',
|
description: 'Get, add and update data in Microsoft SQL',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Microsoft SQL',
|
name: 'Microsoft SQL',
|
||||||
|
@ -250,10 +249,50 @@ export class MicrosoftSql implements INodeType {
|
||||||
await pool.connect();
|
await pool.connect();
|
||||||
|
|
||||||
let responseData: IDataObject | IDataObject[] = [];
|
let responseData: IDataObject | IDataObject[] = [];
|
||||||
|
let returnData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
const operation = this.getNodeParameter('operation', 0);
|
const operation = this.getNodeParameter('operation', 0);
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
if (operation === 'executeQuery' && nodeVersion >= 1.1) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
try {
|
||||||
|
let rawQuery = this.getNodeParameter('query', i) as string;
|
||||||
|
|
||||||
|
for (const resolvable of getResolvables(rawQuery)) {
|
||||||
|
rawQuery = rawQuery.replace(
|
||||||
|
resolvable,
|
||||||
|
this.evaluateExpression(resolvable, i) as string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { recordsets }: IResult<any[]> = await pool.request().query(rawQuery);
|
||||||
|
|
||||||
|
const result: IDataObject[] = recordsets.length > 1 ? flatten(recordsets) : recordsets[0];
|
||||||
|
|
||||||
|
for (const entry of result) {
|
||||||
|
returnData.push({
|
||||||
|
json: entry,
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await pool.close();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await pool.close();
|
||||||
|
return [returnData];
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (operation === 'executeQuery') {
|
if (operation === 'executeQuery') {
|
||||||
let rawQuery = this.getNodeParameter('query', 0) as string;
|
let rawQuery = this.getNodeParameter('query', 0) as string;
|
||||||
|
@ -267,15 +306,19 @@ export class MicrosoftSql implements INodeType {
|
||||||
const result = recordsets.length > 1 ? flatten(recordsets) : recordsets[0];
|
const result = recordsets.length > 1 ? flatten(recordsets) : recordsets[0];
|
||||||
|
|
||||||
responseData = result;
|
responseData = result;
|
||||||
} else if (operation === 'insert') {
|
}
|
||||||
|
|
||||||
|
if (operation === 'insert') {
|
||||||
const tables = createTableStruct(this.getNodeParameter, items);
|
const tables = createTableStruct(this.getNodeParameter, items);
|
||||||
|
|
||||||
await insertOperation(tables, pool);
|
await insertOperation(tables, pool);
|
||||||
|
|
||||||
responseData = items;
|
responseData = items;
|
||||||
} else if (operation === 'update') {
|
}
|
||||||
|
|
||||||
|
if (operation === 'update') {
|
||||||
const updateKeys = items.map(
|
const updateKeys = items.map(
|
||||||
(item, index) => this.getNodeParameter('updateKey', index) as string,
|
(_, index) => this.getNodeParameter('updateKey', index) as string,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tables = createTableStruct(
|
const tables = createTableStruct(
|
||||||
|
@ -288,7 +331,9 @@ export class MicrosoftSql implements INodeType {
|
||||||
await updateOperation(tables, pool);
|
await updateOperation(tables, pool);
|
||||||
|
|
||||||
responseData = items;
|
responseData = items;
|
||||||
} else if (operation === 'delete') {
|
}
|
||||||
|
|
||||||
|
if (operation === 'delete') {
|
||||||
const tables = items.reduce((acc, item, index) => {
|
const tables = items.reduce((acc, item, index) => {
|
||||||
const table = this.getNodeParameter('table', index) as string;
|
const table = this.getNodeParameter('table', index) as string;
|
||||||
const deleteKey = this.getNodeParameter('deleteKey', index) as string;
|
const deleteKey = this.getNodeParameter('deleteKey', index) as string;
|
||||||
|
@ -303,13 +348,14 @@ export class MicrosoftSql implements INodeType {
|
||||||
}, {} as ITables);
|
}, {} as ITables);
|
||||||
|
|
||||||
responseData = await deleteOperation(tables, pool);
|
responseData = await deleteOperation(tables, pool);
|
||||||
} else {
|
|
||||||
await pool.close();
|
|
||||||
throw new NodeOperationError(
|
|
||||||
this.getNode(),
|
|
||||||
`The operation "${operation}" is not supported!`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const itemData = generatePairedItemData(items.length);
|
||||||
|
|
||||||
|
returnData = this.helpers.constructExecutionMetaData(
|
||||||
|
this.helpers.returnJsonArray(responseData),
|
||||||
|
{ itemData },
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (this.continueOnFail()) {
|
if (this.continueOnFail()) {
|
||||||
responseData = items;
|
responseData = items;
|
||||||
|
@ -322,13 +368,6 @@ export class MicrosoftSql implements INodeType {
|
||||||
// shuts down the connection pool associated with the db object to allow the process to finish
|
// shuts down the connection pool associated with the db object to allow the process to finish
|
||||||
await pool.close();
|
await pool.close();
|
||||||
|
|
||||||
const itemData = generatePairedItemData(items.length);
|
return [returnData];
|
||||||
|
|
||||||
const returnItems = this.helpers.constructExecutionMetaData(
|
|
||||||
this.helpers.returnJsonArray(responseData),
|
|
||||||
{ itemData },
|
|
||||||
);
|
|
||||||
|
|
||||||
return [returnItems];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,15 +132,17 @@ export function prepareFields(fields: string) {
|
||||||
.filter((field) => !!field);
|
.filter((field) => !!field);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringifyObjectIDs(items: IDataObject[]) {
|
export function stringifyObjectIDs(items: INodeExecutionData[]) {
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (item._id instanceof ObjectId) {
|
if (item._id instanceof ObjectId) {
|
||||||
item._id = item._id.toString();
|
item.json._id = item._id.toString();
|
||||||
}
|
}
|
||||||
if (item.id instanceof ObjectId) {
|
if (item.id instanceof ObjectId) {
|
||||||
item.id = item.id.toString();
|
item.json.id = item.id.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function connectMongoClient(connectionString: string, credentials: IDataObject = {}) {
|
export async function connectMongoClient(connectionString: string, credentials: IDataObject = {}) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type {
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
JsonObject,
|
JsonObject,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { ApplicationError, NodeOperationError } from 'n8n-workflow';
|
import { ApplicationError } from 'n8n-workflow';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
FindOneAndReplaceOptions,
|
FindOneAndReplaceOptions,
|
||||||
|
@ -38,7 +38,7 @@ export class MongoDb implements INodeType {
|
||||||
name: 'mongoDb',
|
name: 'mongoDb',
|
||||||
icon: 'file:mongodb.svg',
|
icon: 'file:mongodb.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
description: 'Find, insert and update documents in MongoDB',
|
description: 'Find, insert and update documents in MongoDB',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'MongoDB',
|
name: 'MongoDB',
|
||||||
|
@ -108,101 +108,126 @@ export class MongoDb implements INodeType {
|
||||||
|
|
||||||
const mdb = client.db(database);
|
const mdb = client.db(database);
|
||||||
|
|
||||||
let responseData: IDataObject | IDataObject[] = [];
|
let returnData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
const operation = this.getNodeParameter('operation', 0);
|
const operation = this.getNodeParameter('operation', 0);
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
|
||||||
|
let itemsLength = items.length ? 1 : 0;
|
||||||
|
let fallbackPairedItems;
|
||||||
|
|
||||||
|
if (nodeVersion >= 1.1) {
|
||||||
|
itemsLength = items.length;
|
||||||
|
} else {
|
||||||
|
fallbackPairedItems = generatePairedItemData(items.length);
|
||||||
|
}
|
||||||
|
|
||||||
if (operation === 'aggregate') {
|
if (operation === 'aggregate') {
|
||||||
// ----------------------------------
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
// aggregate
|
try {
|
||||||
// ----------------------------------
|
const queryParameter = JSON.parse(
|
||||||
|
this.getNodeParameter('query', i) as string,
|
||||||
|
) as IDataObject;
|
||||||
|
|
||||||
try {
|
if (queryParameter._id && typeof queryParameter._id === 'string') {
|
||||||
const queryParameter = JSON.parse(
|
queryParameter._id = new ObjectId(queryParameter._id);
|
||||||
this.getNodeParameter('query', 0) as string,
|
}
|
||||||
) as IDataObject;
|
|
||||||
|
|
||||||
if (queryParameter._id && typeof queryParameter._id === 'string') {
|
const query = mdb
|
||||||
queryParameter._id = new ObjectId(queryParameter._id);
|
.collection(this.getNodeParameter('collection', i) as string)
|
||||||
}
|
.aggregate(queryParameter as unknown as Document[]);
|
||||||
|
|
||||||
const query = mdb
|
for (const entry of await query.toArray()) {
|
||||||
.collection(this.getNodeParameter('collection', 0) as string)
|
returnData.push({ json: entry, pairedItem: fallbackPairedItems ?? [{ item: i }] });
|
||||||
.aggregate(queryParameter as unknown as Document[]);
|
}
|
||||||
|
} catch (error) {
|
||||||
responseData = await query.toArray();
|
if (this.continueOnFail()) {
|
||||||
} catch (error) {
|
returnData.push({
|
||||||
if (this.continueOnFail()) {
|
json: { error: (error as JsonObject).message },
|
||||||
responseData = [{ error: (error as JsonObject).message }];
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
} else {
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (operation === 'delete') {
|
}
|
||||||
// ----------------------------------
|
|
||||||
// delete
|
|
||||||
// ----------------------------------
|
|
||||||
|
|
||||||
try {
|
if (operation === 'delete') {
|
||||||
const { deletedCount } = await mdb
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
.collection(this.getNodeParameter('collection', 0) as string)
|
try {
|
||||||
.deleteMany(JSON.parse(this.getNodeParameter('query', 0) as string) as Document);
|
const { deletedCount } = await mdb
|
||||||
|
.collection(this.getNodeParameter('collection', i) as string)
|
||||||
|
.deleteMany(JSON.parse(this.getNodeParameter('query', i) as string) as Document);
|
||||||
|
|
||||||
responseData = [{ deletedCount }];
|
returnData.push({
|
||||||
} catch (error) {
|
json: { deletedCount },
|
||||||
if (this.continueOnFail()) {
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
responseData = [{ error: (error as JsonObject).message }];
|
});
|
||||||
} else {
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: (error as JsonObject).message },
|
||||||
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (operation === 'find') {
|
}
|
||||||
// ----------------------------------
|
|
||||||
// find
|
|
||||||
// ----------------------------------
|
|
||||||
|
|
||||||
try {
|
if (operation === 'find') {
|
||||||
const queryParameter = JSON.parse(
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
this.getNodeParameter('query', 0) as string,
|
try {
|
||||||
) as IDataObject;
|
const queryParameter = JSON.parse(
|
||||||
|
this.getNodeParameter('query', i) as string,
|
||||||
|
) as IDataObject;
|
||||||
|
|
||||||
if (queryParameter._id && typeof queryParameter._id === 'string') {
|
if (queryParameter._id && typeof queryParameter._id === 'string') {
|
||||||
queryParameter._id = new ObjectId(queryParameter._id);
|
queryParameter._id = new ObjectId(queryParameter._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = mdb
|
let query = mdb
|
||||||
.collection(this.getNodeParameter('collection', 0) as string)
|
.collection(this.getNodeParameter('collection', i) as string)
|
||||||
.find(queryParameter as unknown as Document);
|
.find(queryParameter as unknown as Document);
|
||||||
|
|
||||||
const options = this.getNodeParameter('options', 0);
|
const options = this.getNodeParameter('options', i);
|
||||||
const limit = options.limit as number;
|
const limit = options.limit as number;
|
||||||
const skip = options.skip as number;
|
const skip = options.skip as number;
|
||||||
const sort = options.sort && (JSON.parse(options.sort as string) as Sort);
|
const sort = options.sort && (JSON.parse(options.sort as string) as Sort);
|
||||||
if (skip > 0) {
|
|
||||||
query = query.skip(skip);
|
|
||||||
}
|
|
||||||
if (limit > 0) {
|
|
||||||
query = query.limit(limit);
|
|
||||||
}
|
|
||||||
if (sort && Object.keys(sort).length !== 0 && sort.constructor === Object) {
|
|
||||||
query = query.sort(sort);
|
|
||||||
}
|
|
||||||
const queryResult = await query.toArray();
|
|
||||||
|
|
||||||
responseData = queryResult;
|
if (skip > 0) {
|
||||||
} catch (error) {
|
query = query.skip(skip);
|
||||||
if (this.continueOnFail()) {
|
}
|
||||||
responseData = [{ error: (error as JsonObject).message }];
|
if (limit > 0) {
|
||||||
} else {
|
query = query.limit(limit);
|
||||||
|
}
|
||||||
|
if (sort && Object.keys(sort).length !== 0 && sort.constructor === Object) {
|
||||||
|
query = query.sort(sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryResult = await query.toArray();
|
||||||
|
|
||||||
|
for (const entry of queryResult) {
|
||||||
|
returnData.push({ json: entry, pairedItem: fallbackPairedItems ?? [{ item: i }] });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: (error as JsonObject).message },
|
||||||
|
pairedItem: fallbackPairedItems ?? [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (operation === 'findOneAndReplace') {
|
}
|
||||||
// ----------------------------------
|
|
||||||
// findOneAndReplace
|
|
||||||
// ----------------------------------
|
|
||||||
|
|
||||||
|
if (operation === 'findOneAndReplace') {
|
||||||
|
fallbackPairedItems = fallbackPairedItems ?? generatePairedItemData(items.length);
|
||||||
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
||||||
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
||||||
const dateFields = prepareFields(
|
const dateFields = prepareFields(
|
||||||
|
@ -237,12 +262,14 @@ export class MongoDb implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = updateItems;
|
returnData = this.helpers.constructExecutionMetaData(
|
||||||
} else if (operation === 'findOneAndUpdate') {
|
this.helpers.returnJsonArray(updateItems),
|
||||||
// ----------------------------------
|
{ itemData: fallbackPairedItems },
|
||||||
// findOneAndUpdate
|
);
|
||||||
// ----------------------------------
|
}
|
||||||
|
|
||||||
|
if (operation === 'findOneAndUpdate') {
|
||||||
|
fallbackPairedItems = fallbackPairedItems ?? generatePairedItemData(items.length);
|
||||||
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
||||||
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
||||||
const dateFields = prepareFields(
|
const dateFields = prepareFields(
|
||||||
|
@ -277,11 +304,15 @@ export class MongoDb implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = updateItems;
|
returnData = this.helpers.constructExecutionMetaData(
|
||||||
} else if (operation === 'insert') {
|
this.helpers.returnJsonArray(updateItems),
|
||||||
// ----------------------------------
|
{ itemData: fallbackPairedItems },
|
||||||
// insert
|
);
|
||||||
// ----------------------------------
|
}
|
||||||
|
|
||||||
|
if (operation === 'insert') {
|
||||||
|
fallbackPairedItems = fallbackPairedItems ?? generatePairedItemData(items.length);
|
||||||
|
let responseData: IDataObject[] = [];
|
||||||
try {
|
try {
|
||||||
// Prepare the data to insert and copy it to be returned
|
// Prepare the data to insert and copy it to be returned
|
||||||
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
||||||
|
@ -310,11 +341,15 @@ export class MongoDb implements INodeType {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (operation === 'update') {
|
|
||||||
// ----------------------------------
|
|
||||||
// update
|
|
||||||
// ----------------------------------
|
|
||||||
|
|
||||||
|
returnData = this.helpers.constructExecutionMetaData(
|
||||||
|
this.helpers.returnJsonArray(responseData),
|
||||||
|
{ itemData: fallbackPairedItems },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'update') {
|
||||||
|
fallbackPairedItems = fallbackPairedItems ?? generatePairedItemData(items.length);
|
||||||
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
const fields = prepareFields(this.getNodeParameter('fields', 0) as string);
|
||||||
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
const useDotNotation = this.getNodeParameter('options.useDotNotation', 0, false) as boolean;
|
||||||
const dateFields = prepareFields(
|
const dateFields = prepareFields(
|
||||||
|
@ -349,30 +384,14 @@ export class MongoDb implements INodeType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = updateItems;
|
returnData = this.helpers.constructExecutionMetaData(
|
||||||
} else {
|
this.helpers.returnJsonArray(updateItems),
|
||||||
if (this.continueOnFail()) {
|
{ itemData: fallbackPairedItems },
|
||||||
responseData = [{ error: `The operation "${operation}" is not supported!` }];
|
);
|
||||||
} else {
|
|
||||||
throw new NodeOperationError(
|
|
||||||
this.getNode(),
|
|
||||||
`The operation "${operation}" is not supported!`,
|
|
||||||
{ itemIndex: 0 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.close();
|
await client.close();
|
||||||
|
|
||||||
stringifyObjectIDs(responseData);
|
return [stringifyObjectIDs(returnData)];
|
||||||
|
|
||||||
const itemData = generatePairedItemData(items.length);
|
|
||||||
|
|
||||||
const returnItems = this.helpers.constructExecutionMetaData(
|
|
||||||
this.helpers.returnJsonArray(responseData),
|
|
||||||
{ itemData },
|
|
||||||
);
|
|
||||||
|
|
||||||
return [returnItems];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class RssFeedRead implements INodeType {
|
||||||
name: 'rssFeedRead',
|
name: 'rssFeedRead',
|
||||||
icon: 'fa:rss',
|
icon: 'fa:rss',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: [1, 1.1],
|
||||||
description: 'Reads data from an RSS Feed',
|
description: 'Reads data from an RSS Feed',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'RSS Read',
|
name: 'RSS Read',
|
||||||
|
@ -65,59 +65,88 @@ export class RssFeedRead implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const pairedItem = generatePairedItemData(this.getInputData().length);
|
const returnData: INodeExecutionData[] = [];
|
||||||
|
const nodeVersion = this.getNode().typeVersion;
|
||||||
|
const items = this.getInputData();
|
||||||
|
|
||||||
try {
|
let itemsLength = items.length ? 1 : 0;
|
||||||
const url = this.getNodeParameter('url', 0) as string;
|
let fallbackPairedItems;
|
||||||
const options = this.getNodeParameter('options', 0);
|
|
||||||
const ignoreSSL = Boolean(options.ignoreSSL);
|
|
||||||
|
|
||||||
if (!url) {
|
if (nodeVersion >= 1.1) {
|
||||||
throw new NodeOperationError(this.getNode(), 'The parameter "URL" has to be set!');
|
itemsLength = items.length;
|
||||||
}
|
} else {
|
||||||
|
fallbackPairedItems = generatePairedItemData(items.length);
|
||||||
|
}
|
||||||
|
|
||||||
if (!validateURL(url)) {
|
for (let i = 0; i < itemsLength; i++) {
|
||||||
throw new NodeOperationError(this.getNode(), 'The provided "URL" is not valid!');
|
|
||||||
}
|
|
||||||
|
|
||||||
const parser = new Parser({
|
|
||||||
requestOptions: {
|
|
||||||
rejectUnauthorized: !ignoreSSL,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let feed: Parser.Output<IDataObject>;
|
|
||||||
try {
|
try {
|
||||||
feed = await parser.parseURL(url);
|
const url = this.getNodeParameter('url', i) as string;
|
||||||
} catch (error) {
|
const options = this.getNodeParameter('options', i);
|
||||||
if (error.code === 'ECONNREFUSED') {
|
const ignoreSSL = Boolean(options.ignoreSSL);
|
||||||
throw new NodeOperationError(
|
|
||||||
this.getNode(),
|
if (!url) {
|
||||||
`It was not possible to connect to the URL. Please make sure the URL "${url}" it is valid!`,
|
throw new NodeOperationError(this.getNode(), 'The parameter "URL" has to be set!', {
|
||||||
);
|
itemIndex: i,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NodeOperationError(this.getNode(), error as Error);
|
if (!validateURL(url)) {
|
||||||
}
|
throw new NodeOperationError(this.getNode(), 'The provided "URL" is not valid!', {
|
||||||
|
itemIndex: i,
|
||||||
const returnData: INodeExecutionData[] = [];
|
|
||||||
|
|
||||||
// For now we just take the items and ignore everything else
|
|
||||||
if (feed.items) {
|
|
||||||
feed.items.forEach((item) => {
|
|
||||||
returnData.push({
|
|
||||||
json: item,
|
|
||||||
pairedItem,
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return [returnData];
|
const parser = new Parser({
|
||||||
} catch (error) {
|
requestOptions: {
|
||||||
if (this.continueOnFail()) {
|
rejectUnauthorized: !ignoreSSL,
|
||||||
return [[{ json: { error: error.message }, pairedItem }]];
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let feed: Parser.Output<IDataObject>;
|
||||||
|
try {
|
||||||
|
feed = await parser.parseURL(url);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ECONNREFUSED') {
|
||||||
|
throw new NodeOperationError(
|
||||||
|
this.getNode(),
|
||||||
|
`It was not possible to connect to the URL. Please make sure the URL "${url}" it is valid!`,
|
||||||
|
{
|
||||||
|
itemIndex: i,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NodeOperationError(this.getNode(), error as Error, {
|
||||||
|
itemIndex: i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now we just take the items and ignore everything else
|
||||||
|
if (feed.items) {
|
||||||
|
const feedItems = (feed.items as IDataObject[]).map((item) => ({
|
||||||
|
json: item,
|
||||||
|
})) as INodeExecutionData[];
|
||||||
|
|
||||||
|
const itemData = fallbackPairedItems || [{ item: i }];
|
||||||
|
|
||||||
|
const executionData = this.helpers.constructExecutionMetaData(feedItems, {
|
||||||
|
itemData,
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.push(...executionData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: { error: error.message },
|
||||||
|
pairedItem: fallbackPairedItems || [{ item: i }],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [returnData];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue