mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(Mindee Node): Add support for new version (#3596)
* Added versioning, with new credentials * Fix lint issue * 🔥 Remove old import name * Change function name * 🎨 Simplify name of versions within the node mindee * 🎨 Change version type from string to number * 🔥 Removed Mindee from getting ignored by prettier * 🎨 Ran prettier on Mindee folder * 🔥 Removed unused import * 🎨 Moved exceptions that were not working anymore Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
@@ -140,7 +140,6 @@ packages/nodes-base/nodes/Medium
|
|||||||
packages/nodes-base/nodes/Merge
|
packages/nodes-base/nodes/Merge
|
||||||
packages/nodes-base/nodes/MessageBird
|
packages/nodes-base/nodes/MessageBird
|
||||||
packages/nodes-base/nodes/Microsoft
|
packages/nodes-base/nodes/Microsoft
|
||||||
packages/nodes-base/nodes/Mindee
|
|
||||||
packages/nodes-base/nodes/Misp
|
packages/nodes-base/nodes/Misp
|
||||||
packages/nodes-base/nodes/Mocean
|
packages/nodes-base/nodes/Mocean
|
||||||
packages/nodes-base/nodes/MondayCom
|
packages/nodes-base/nodes/MondayCom
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
IHttpRequestOptions,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
@@ -15,4 +17,14 @@ export class MindeeInvoiceApi implements ICredentialType {
|
|||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
|
||||||
|
// @ts-ignore
|
||||||
|
const url = requestOptions.url ? requestOptions.url : requestOptions.uri;
|
||||||
|
if(url.includes('https://api.mindee.net/v1/')) {
|
||||||
|
requestOptions.headers!['Authorization'] = `Token ${credentials.apiKey}`;
|
||||||
|
} else {
|
||||||
|
requestOptions.headers!['X-Inferuser-Token'] = `${credentials.apiKey}`;
|
||||||
|
}
|
||||||
|
return requestOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
IHttpRequestOptions,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
@@ -15,4 +17,14 @@ export class MindeeReceiptApi implements ICredentialType {
|
|||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
|
||||||
|
// @ts-ignore
|
||||||
|
const url = requestOptions.url ? requestOptions.url : requestOptions.uri;
|
||||||
|
if(url.includes('https://api.mindee.net/v1/')) {
|
||||||
|
requestOptions.headers!['Authorization'] = `Token ${credentials.apiKey}`;
|
||||||
|
} else {
|
||||||
|
requestOptions.headers!['X-Inferuser-Token'] = `${credentials.apiKey}`;
|
||||||
|
}
|
||||||
|
return requestOptions;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,42 @@
|
|||||||
import {
|
import { OptionsWithUri } from 'request';
|
||||||
OptionsWithUri,
|
|
||||||
} from 'request';
|
|
||||||
|
|
||||||
import {
|
import { IExecuteFunctions, IExecuteSingleFunctions, ILoadOptionsFunctions } from 'n8n-core';
|
||||||
IExecuteFunctions,
|
|
||||||
IExecuteSingleFunctions,
|
|
||||||
ILoadOptionsFunctions,
|
|
||||||
} from 'n8n-core';
|
|
||||||
|
|
||||||
import {
|
import { IDataObject, NodeApiError } from 'n8n-workflow';
|
||||||
IDataObject, NodeApiError,
|
|
||||||
} from 'n8n-workflow';
|
export async function mindeeApiRequest(
|
||||||
|
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||||
|
method: string,
|
||||||
|
path: string,
|
||||||
|
body: any = {}, // tslint:disable-line:no-any
|
||||||
|
qs: IDataObject = {},
|
||||||
|
option = {},
|
||||||
|
): Promise<any> {// tslint:disable-line:no-any
|
||||||
|
|
||||||
export async function mindeeApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise<any> { // tslint:disable-line:no-any
|
|
||||||
|
|
||||||
const resource = this.getNodeParameter('resource', 0) as string;
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
|
|
||||||
let credentials;
|
let service;
|
||||||
|
|
||||||
if (resource === 'receipt') {
|
if (resource === 'receipt') {
|
||||||
credentials = await this.getCredentials('mindeeReceiptApi');
|
service = 'mindeeReceiptApi';
|
||||||
} else {
|
} else {
|
||||||
credentials = await this.getCredentials('mindeeInvoiceApi');
|
service = 'mindeeInvoiceApi';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const version = this.getNodeParameter('apiVersion', 0) as number;
|
||||||
|
// V1 of mindee is deprecated, we are keeping it for now but now V3 is active
|
||||||
|
const url =
|
||||||
|
version === 1
|
||||||
|
? `https://api.mindee.net/products${path}`
|
||||||
|
: `https://api.mindee.net/v1/products/mindee${path}`;
|
||||||
|
|
||||||
const options: OptionsWithUri = {
|
const options: OptionsWithUri = {
|
||||||
headers: {
|
headers: {},
|
||||||
'X-Inferuser-Token': credentials.apiKey,
|
|
||||||
},
|
|
||||||
method,
|
method,
|
||||||
body,
|
body,
|
||||||
qs,
|
qs,
|
||||||
uri: `https://api.mindee.net/products${path}`,
|
uri: url,
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -44,19 +49,17 @@ export async function mindeeApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
|||||||
if (Object.keys(option).length !== 0) {
|
if (Object.keys(option).length !== 0) {
|
||||||
Object.assign(options, option);
|
Object.assign(options, option);
|
||||||
}
|
}
|
||||||
//@ts-ignore
|
|
||||||
return await this.helpers.request.call(this, options);
|
return await this.helpers.requestWithAuthentication.call(this, service, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new NodeApiError(this.getNode(), error);
|
throw new NodeApiError(this.getNode(), error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanData(predictions: IDataObject[]) {
|
export function cleanDataPreviousApiVersions(predictions: IDataObject[]) {
|
||||||
|
|
||||||
const newData: IDataObject = {};
|
const newData: IDataObject = {};
|
||||||
|
|
||||||
for (const key of Object.keys(predictions[0])) {
|
for (const key of Object.keys(predictions[0])) {
|
||||||
|
|
||||||
const data = predictions[0][key] as IDataObject | IDataObject[];
|
const data = predictions[0][key] as IDataObject | IDataObject[];
|
||||||
|
|
||||||
if (key === 'taxes' && data.length) {
|
if (key === 'taxes' && data.length) {
|
||||||
@@ -65,13 +68,46 @@ export function cleanData(predictions: IDataObject[]) {
|
|||||||
rate: (data as IDataObject[])[0].rate,
|
rate: (data as IDataObject[])[0].rate,
|
||||||
};
|
};
|
||||||
} else if (key === 'locale') {
|
} else if (key === 'locale') {
|
||||||
//@ts-ignore
|
|
||||||
newData['currency'] = data.currency;
|
|
||||||
//@ts-ignore
|
|
||||||
newData['locale'] = data.value;
|
|
||||||
} else {
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
newData[key] = data.value || data.name || data.raw || data.degrees || data.amount || data.iban;
|
newData['currency'] = data.currency;
|
||||||
|
//@ts-ignore
|
||||||
|
newData['locale'] = data.value;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
newData[key] =
|
||||||
|
//@ts-ignore
|
||||||
|
data.value || data.name || data.raw || data.degrees || data.amount || data.iban;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanData(document: IDataObject) {
|
||||||
|
// @ts-ignore
|
||||||
|
const prediction = document.inference.prediction as IDataObject;
|
||||||
|
const newData: IDataObject = {};
|
||||||
|
newData['id'] = document.id;
|
||||||
|
newData['name'] = document.name;
|
||||||
|
newData['number_of_pages'] = document.n_pages;
|
||||||
|
for (const key of Object.keys(prediction)) {
|
||||||
|
const data = prediction[key] as IDataObject | IDataObject[];
|
||||||
|
|
||||||
|
if (key === 'taxes' && data.length) {
|
||||||
|
newData[key] = {
|
||||||
|
amount: (data as IDataObject[])[0].amount,
|
||||||
|
rate: (data as IDataObject[])[0].rate,
|
||||||
|
};
|
||||||
|
} else if (key === 'locale') {
|
||||||
|
//@ts-ignore
|
||||||
|
newData['currency'] = data.currency;
|
||||||
|
//@ts-ignore
|
||||||
|
newData['locale'] = data.value;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
newData[key] =
|
||||||
|
//@ts-ignore
|
||||||
|
data.value || data.name || data.raw || data.degrees || data.amount || data.iban;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
"node": "n8n-nodes-base.mindee",
|
"node": "n8n-nodes-base.mindee",
|
||||||
"nodeVersion": "1.0",
|
"nodeVersion": "1.0",
|
||||||
"codexVersion": "1.0",
|
"codexVersion": "1.0",
|
||||||
"categories": [
|
"categories": ["Utility"],
|
||||||
"Utility"
|
|
||||||
],
|
|
||||||
"resources": {
|
"resources": {
|
||||||
"credentialDocumentation": [
|
"credentialDocumentation": [
|
||||||
{
|
{
|
||||||
@@ -29,4 +27,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import {
|
import { IExecuteFunctions } from 'n8n-core';
|
||||||
IExecuteFunctions,
|
|
||||||
} from 'n8n-core';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IBinaryData,
|
IBinaryData,
|
||||||
@@ -12,10 +10,7 @@ import {
|
|||||||
NodeOperationError,
|
NodeOperationError,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import { cleanData, cleanDataPreviousApiVersions, mindeeApiRequest } from './GenericFunctions';
|
||||||
cleanData,
|
|
||||||
mindeeApiRequest,
|
|
||||||
} from './GenericFunctions';
|
|
||||||
|
|
||||||
export class Mindee implements INodeType {
|
export class Mindee implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
@@ -23,7 +18,7 @@ export class Mindee implements INodeType {
|
|||||||
name: 'mindee',
|
name: 'mindee',
|
||||||
icon: 'file:mindee.svg',
|
icon: 'file:mindee.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: [1, 2],
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
description: 'Consume Mindee API',
|
description: 'Consume Mindee API',
|
||||||
defaults: {
|
defaults: {
|
||||||
@@ -37,9 +32,7 @@ export class Mindee implements INodeType {
|
|||||||
required: true,
|
required: true,
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: ['receipt'],
|
||||||
'receipt',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -48,14 +41,58 @@ export class Mindee implements INodeType {
|
|||||||
required: true,
|
required: true,
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [
|
resource: ['invoice'],
|
||||||
'invoice',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
|
{
|
||||||
|
displayName: 'API Version',
|
||||||
|
name: 'apiVersion',
|
||||||
|
type: 'options',
|
||||||
|
isNodeSetting: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'@version': [1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: '1',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '3',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 1,
|
||||||
|
description: 'Whether to return all results or only up to a given limit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'API Version',
|
||||||
|
name: 'apiVersion',
|
||||||
|
type: 'options',
|
||||||
|
isNodeSetting: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'@version': [2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: '1',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '3',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 3,
|
||||||
|
description: 'Whether to return all results or only up to a given limit',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Resource',
|
displayName: 'Resource',
|
||||||
name: 'resource',
|
name: 'resource',
|
||||||
@@ -94,16 +131,12 @@ export class Mindee implements INodeType {
|
|||||||
default: 'data',
|
default: 'data',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
operation: [
|
operation: ['predict'],
|
||||||
'predict',
|
resource: ['receipt', 'invoice'],
|
||||||
],
|
|
||||||
resource: [
|
|
||||||
'receipt',
|
|
||||||
'invoice',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description: 'Name of the binary property which containsthe data for the file to be uploaded',
|
description:
|
||||||
|
'Name of the binary property which containsthe data for the file to be uploaded',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'RAW Data',
|
displayName: 'RAW Data',
|
||||||
@@ -121,8 +154,10 @@ export class Mindee implements INodeType {
|
|||||||
const length = items.length;
|
const length = items.length;
|
||||||
const qs: IDataObject = {};
|
const qs: IDataObject = {};
|
||||||
let responseData;
|
let responseData;
|
||||||
|
const version = this.getNodeParameter('apiVersion', 0) as number;
|
||||||
const resource = this.getNodeParameter('resource', 0) as string;
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
let endpoint;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
try {
|
try {
|
||||||
if (resource === 'receipt') {
|
if (resource === 'receipt') {
|
||||||
@@ -141,29 +176,55 @@ export class Mindee implements INodeType {
|
|||||||
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
||||||
|
|
||||||
if (binaryData === undefined) {
|
if (binaryData === undefined) {
|
||||||
throw new NodeOperationError(this.getNode(), `No binary data property "${binaryPropertyName}" does not exists on item!`, { itemIndex: i });
|
throw new NodeOperationError(
|
||||||
|
this.getNode(),
|
||||||
|
`No binary data property "${binaryPropertyName}" does not exists on item!`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
if (version === 1) {
|
||||||
responseData = await mindeeApiRequest.call(
|
responseData = await mindeeApiRequest.call(
|
||||||
this,
|
this,
|
||||||
'POST',
|
'POST',
|
||||||
`/expense_receipts/v2/predict`,
|
`/expense_receipts/v2/predict`,
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
formData: {
|
formData: {
|
||||||
file: {
|
file: {
|
||||||
value: dataBuffer,
|
value: dataBuffer,
|
||||||
options: {
|
options: {
|
||||||
filename: binaryData.fileName,
|
filename: binaryData.fileName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
} else if (version === 3) {
|
||||||
|
endpoint = '/expense_receipts/v3/predict';
|
||||||
|
responseData = await mindeeApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
endpoint,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
formData: {
|
||||||
|
document: {
|
||||||
|
value: dataBuffer,
|
||||||
|
options: {
|
||||||
|
filename: binaryData.fileName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
if (rawData === false) {
|
if (rawData === false) {
|
||||||
responseData = cleanData(responseData.predictions);
|
if (version === 1) {
|
||||||
|
responseData = cleanDataPreviousApiVersions(responseData.predictions);
|
||||||
|
} else if (version === 3) {
|
||||||
|
responseData = cleanData(responseData.document);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,29 +245,58 @@ export class Mindee implements INodeType {
|
|||||||
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
||||||
|
|
||||||
if (binaryData === undefined) {
|
if (binaryData === undefined) {
|
||||||
throw new NodeOperationError(this.getNode(), `No binary data property "${binaryPropertyName}" does not exists on item!`, { itemIndex: i });
|
throw new NodeOperationError(
|
||||||
|
this.getNode(),
|
||||||
|
`No binary data property "${binaryPropertyName}" does not exists on item!`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
if (version === 1) {
|
||||||
responseData = await mindeeApiRequest.call(
|
endpoint = '/invoices/v1/predict';
|
||||||
this,
|
responseData = await mindeeApiRequest.call(
|
||||||
'POST',
|
this,
|
||||||
`/invoices/v1/predict`,
|
'POST',
|
||||||
{},
|
endpoint,
|
||||||
{},
|
{},
|
||||||
{
|
{},
|
||||||
formData: {
|
{
|
||||||
file: {
|
formData: {
|
||||||
value: dataBuffer,
|
file: {
|
||||||
options: {
|
value: dataBuffer,
|
||||||
filename: binaryData.fileName,
|
options: {
|
||||||
|
filename: binaryData.fileName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
} else if (version === 3) {
|
||||||
|
endpoint = '/invoices/v3/predict';
|
||||||
|
responseData = await mindeeApiRequest.call(
|
||||||
|
this,
|
||||||
|
'POST',
|
||||||
|
endpoint,
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
formData: {
|
||||||
|
document: {
|
||||||
|
value: dataBuffer,
|
||||||
|
options: {
|
||||||
|
filename: binaryData.fileName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new NodeOperationError(this.getNode(), 'Invalid API version');
|
||||||
|
}
|
||||||
if (rawData === false) {
|
if (rawData === false) {
|
||||||
responseData = cleanData(responseData.predictions);
|
if (version === 1) {
|
||||||
|
responseData = cleanDataPreviousApiVersions(responseData.predictions);
|
||||||
|
} else if (version === 3) {
|
||||||
|
responseData = cleanData(responseData.document);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user