mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
feat(Metabase Node): Add Metabase Node (#3033)
* Boilerplate with new node's version for metabse * Metabases MVP features * Added new credential for metabse, added custom auth for metabase * Fixed bug with one enpoint not working * Clean up code * Uniformised the renovate token * Made two example of responses for review * Fixed lint issues * Feature add datasources * Changed output from databases * Changed questions data output * Fixed issue when testing credentials with new node format * Add the possibility to get raw data * Removed handle for the metabase meta results, changed export's name * Add binary extraction for the result data * Fixed binary download issue * ⚡ Add preAuthentication method to credentials * Revert "Added new credential for metabse, added custom auth for metabase" This reverts commit5f1b7607ad
. * Revert "Added new credential for metabse, added custom auth for metabase" This reverts commit5f1b7607ad
. * Added preAuth and fixed autfixable linting rules * Fixed linting errors * Linting fixes * Remove / at the end of url, and add placeholder for cred url * Make export to Json retun only json and no binary * Fix lint issues * Add action and exception for lint rule * Remove unnecessary credential file * ⚡ Simplify and cleanup Co-authored-by: ricardo <ricardoespinoza105@gmail.com> Co-authored-by: Omar Ajoue <krynble@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
b4525d06ea
commit
81b5828558
|
@ -92,7 +92,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||
if (credentialType.authenticate) {
|
||||
if (typeof credentialType.authenticate === 'function') {
|
||||
// Special authentication function is defined
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
return credentialType.authenticate(credentials, requestOptions as IHttpRequestOptions);
|
||||
}
|
||||
|
@ -559,7 +558,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||
nodeToTestWith?: string,
|
||||
): Promise<INodeCredentialTestResult> {
|
||||
const credentialTestFunction = this.getCredentialTestFunction(credentialType, nodeToTestWith);
|
||||
|
||||
if (credentialTestFunction === undefined) {
|
||||
return Promise.resolve({
|
||||
status: 'Error',
|
||||
|
@ -690,7 +688,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||
statusCode: error.cause.response.status,
|
||||
statusMessage: error.cause.response.statusText,
|
||||
};
|
||||
|
||||
if (credentialTestFunction.testRequest.rules) {
|
||||
// Special testing rules are defined so check all in order
|
||||
for (const rule of credentialTestFunction.testRequest.rules) {
|
||||
|
@ -716,6 +713,11 @@ export class CredentialsHelper extends ICredentialsHelper {
|
|||
`Received HTTP status code: ${errorResponseData.statusCode}`,
|
||||
};
|
||||
}
|
||||
} else if (error.cause.code) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: error.cause.code,
|
||||
};
|
||||
}
|
||||
Logger.debug('Credential test failed', error);
|
||||
return {
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
ICredentialTestRequest,
|
||||
ICredentialType,
|
||||
IHttpRequestHelper,
|
||||
IHttpRequestOptions,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
|
59
packages/nodes-base/nodes/Metabase/AlertsDescription.ts
Normal file
59
packages/nodes-base/nodes/Metabase/AlertsDescription.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const alertsOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['alerts'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get specific alert',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '={{"/api/alert/" + $parameter.alertId}}',
|
||||
},
|
||||
},
|
||||
action: 'Get an alert',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all the alerts',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/api/alert/',
|
||||
},
|
||||
},
|
||||
action: 'Get all alerts',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
];
|
||||
|
||||
export const alertsFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Alert ID',
|
||||
name: 'alertId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: '0',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['alerts'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
355
packages/nodes-base/nodes/Metabase/DatabasesDescription.ts
Normal file
355
packages/nodes-base/nodes/Metabase/DatabasesDescription.ts
Normal file
|
@ -0,0 +1,355 @@
|
|||
import { IN8nHttpFullResponse, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const databasesOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'addNewDatasource',
|
||||
description: 'Add a new datasource to the metabase instance',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '/api/database',
|
||||
},
|
||||
},
|
||||
action: 'Add a databases',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all the databases',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/api/database/',
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
{
|
||||
type: 'rootProperty',
|
||||
properties: {
|
||||
property: 'data',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Get all databases',
|
||||
},
|
||||
{
|
||||
name: 'Get Fields',
|
||||
value: 'getFields',
|
||||
description: 'Get fields from database',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '={{"/api/database/" + $parameter.databaseId + "/fields"}}',
|
||||
},
|
||||
},
|
||||
action: 'Get Fields a databases',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
];
|
||||
|
||||
export const databasesFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Database ID',
|
||||
name: 'databaseId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: '0',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['getFields'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Engine',
|
||||
name: 'engine',
|
||||
type: 'options',
|
||||
required: true,
|
||||
placeholder: 'PostgreSQL',
|
||||
options: [
|
||||
{
|
||||
name: 'H2',
|
||||
value: 'h2',
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
value: 'mongo',
|
||||
},
|
||||
{
|
||||
name: 'Mysql',
|
||||
value: 'mysql',
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
value: 'postgres',
|
||||
},
|
||||
{
|
||||
name: 'Redshift',
|
||||
value: 'redshift',
|
||||
},
|
||||
{
|
||||
name: 'Sqlite',
|
||||
value: 'sqlite',
|
||||
},
|
||||
],
|
||||
default: 'postgres',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'engine',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Host',
|
||||
name: 'host',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: 'localhost:5432',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['postgres', 'redshift', 'mysql', 'mongo'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.host',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: 'Database 1',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'name',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Port',
|
||||
name: 'port',
|
||||
type: 'number',
|
||||
required: true,
|
||||
placeholder: '5432',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['postgres', 'redshift', 'mysql', 'mongo'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.port',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: 5432,
|
||||
},
|
||||
{
|
||||
displayName: 'User',
|
||||
name: 'user',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: 'Admin',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['postgres', 'redshift', 'mysql', 'mongo'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.user',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Password',
|
||||
name: 'password',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: 'password',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['postgres', 'redshift', 'mysql', 'mongo'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.password',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Database Name',
|
||||
name: 'dbName',
|
||||
type: 'string',
|
||||
placeholder: 'Users',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['postgres', 'redshift', 'mysql', 'mongo'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.db',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'File Path',
|
||||
name: 'filePath',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: 'file:/Users/admin/Desktop/Users',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
engine: ['h2', 'sqlite'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'details.db',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Full Sync',
|
||||
name: 'fullSync',
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
default: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['addNewDatasource'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
send: {
|
||||
property: 'is_full_sync',
|
||||
type: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
description: 'Whether to return a simplified version of the response instead of the raw data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['databases'],
|
||||
operation: ['getAll'],
|
||||
},
|
||||
},
|
||||
routing: {
|
||||
output: {
|
||||
postReceive: [
|
||||
{
|
||||
type: 'setKeyValue',
|
||||
enabled: '={{$value}}',
|
||||
properties: {
|
||||
id: '={{$responseItem.id}}',
|
||||
name: '={{$responseItem.name}}',
|
||||
description: '={{$responseItem.description}}',
|
||||
engine: '={{$responseItem.engine}}',
|
||||
creator_id: '={{$responseItem.creator_id}}',
|
||||
timezone: '={{$responseItem.timezone}}',
|
||||
created_at: '={{$responseItem.created_at}}',
|
||||
updated_at: '={{$responseItem.updated_at}}',
|
||||
db: '={{$responseItem.details.db}}',
|
||||
user: '={{$responseItem.details.user}}',
|
||||
host: '={{$responseItem.details.host}}',
|
||||
port: '={{$responseItem.details.port}}',
|
||||
ssl: '={{$responseItem.details.ssl}}',
|
||||
is_full_sync: '={{$responseItem.details.is_full_sync}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
default: true,
|
||||
},
|
||||
];
|
||||
|
||||
type MetabaseDatabaseResult = IN8nHttpFullResponse & {
|
||||
body: Array<{
|
||||
data: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
details: MetabaseDatabaseDetail;
|
||||
timezone: string;
|
||||
creator_id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
engine: string;
|
||||
is_full_sync: string;
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
type MetabaseDatabaseDetail = {
|
||||
host?: string;
|
||||
port?: number;
|
||||
user?: string;
|
||||
ssl?: boolean;
|
||||
db?: string;
|
||||
};
|
18
packages/nodes-base/nodes/Metabase/Metabase.node.json
Normal file
18
packages/nodes-base/nodes/Metabase/Metabase.node.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.metabase",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Development", "Data & Storage"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/metabase"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.metabse/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
73
packages/nodes-base/nodes/Metabase/Metabase.node.ts
Normal file
73
packages/nodes-base/nodes/Metabase/Metabase.node.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { INodeType, INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import { questionsFields, questionsOperations } from './QuestionsDescription';
|
||||
|
||||
import { metricsFields, metricsOperations } from './MetricsDescription';
|
||||
|
||||
import { databasesFields, databasesOperations } from './DatabasesDescription';
|
||||
|
||||
import { alertsFields, alertsOperations } from './AlertsDescription';
|
||||
|
||||
export class Metabase implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Metabase',
|
||||
name: 'metabase',
|
||||
icon: 'file:metabase.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Use the Metabase API',
|
||||
defaults: {
|
||||
name: 'Metabase',
|
||||
color: '#ff0000',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'metabaseApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
requestDefaults: {
|
||||
returnFullResponse: true,
|
||||
baseURL: '={{$credentials.url.replace(new RegExp("/$"), "")}}',
|
||||
headers: {},
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Alert',
|
||||
value: 'alerts',
|
||||
},
|
||||
{
|
||||
name: 'Database',
|
||||
value: 'databases',
|
||||
},
|
||||
{
|
||||
name: 'Metric',
|
||||
value: 'metrics',
|
||||
},
|
||||
{
|
||||
name: 'Question',
|
||||
value: 'questions',
|
||||
},
|
||||
],
|
||||
default: 'questions',
|
||||
},
|
||||
...questionsOperations,
|
||||
...questionsFields,
|
||||
...metricsOperations,
|
||||
...metricsFields,
|
||||
...databasesOperations,
|
||||
...databasesFields,
|
||||
...alertsOperations,
|
||||
...alertsFields,
|
||||
],
|
||||
};
|
||||
}
|
60
packages/nodes-base/nodes/Metabase/MetricsDescription.ts
Normal file
60
packages/nodes-base/nodes/Metabase/MetricsDescription.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const metricsOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['metrics'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a specific metric',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '={{"/api/metric/" + $parameter.metricId}}',
|
||||
returnFullResponse: true,
|
||||
},
|
||||
},
|
||||
action: 'Get a metric',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all the metrics',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/api/metric/',
|
||||
},
|
||||
},
|
||||
action: 'Get all metrics',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Metric ID',
|
||||
name: 'metricId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: '0',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['metrics'],
|
||||
operation: ['get'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
];
|
145
packages/nodes-base/nodes/Metabase/QuestionsDescription.ts
Normal file
145
packages/nodes-base/nodes/Metabase/QuestionsDescription.ts
Normal file
|
@ -0,0 +1,145 @@
|
|||
import {
|
||||
IDataObject,
|
||||
IExecuteSingleFunctions,
|
||||
IN8nHttpFullResponse,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const questionsOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['questions'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a specific question',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '={{"/api/card/" + $parameter.questionId}}',
|
||||
},
|
||||
},
|
||||
action: 'Get a questions',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all the questions',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/api/card/',
|
||||
},
|
||||
},
|
||||
action: 'Get all questions',
|
||||
},
|
||||
{
|
||||
name: 'Result Data',
|
||||
value: 'resultData',
|
||||
description: 'Return the result of the question to a specific file format',
|
||||
routing: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: '={{"/api/card/" + $parameter.questionId + "/query/" + $parameter.format}}',
|
||||
returnFullResponse: true,
|
||||
encoding: 'arraybuffer',
|
||||
},
|
||||
output: {
|
||||
postReceive: [
|
||||
// @ts-ignore
|
||||
async function (
|
||||
this: IExecuteSingleFunctions,
|
||||
_items: INodeExecutionData[],
|
||||
response: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const items = _items;
|
||||
const result: INodeExecutionData[] = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[i].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (items[i].binary !== undefined) {
|
||||
Object.assign(newItem.binary, items[i].binary);
|
||||
}
|
||||
items[i] = newItem;
|
||||
if (this.getNode().parameters.format === 'json') {
|
||||
items[i].json = JSON.parse(
|
||||
items[i].json as unknown as string,
|
||||
)[0] as unknown as IDataObject;
|
||||
console.log(items[i].json);
|
||||
delete items[i].binary;
|
||||
} else {
|
||||
items[i].binary!['data'] = await this.helpers.prepareBinaryData(
|
||||
response.body as Buffer,
|
||||
'data',
|
||||
response.headers['content-type'],
|
||||
);
|
||||
}
|
||||
result.push(items[i]);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
action: 'Result Data a questions',
|
||||
},
|
||||
],
|
||||
default: 'getAll',
|
||||
},
|
||||
];
|
||||
|
||||
export const questionsFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Question ID',
|
||||
name: 'questionId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
placeholder: '0',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['questions'],
|
||||
operation: ['get', 'resultData'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Format',
|
||||
name: 'format',
|
||||
type: 'options',
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
name: 'CSV',
|
||||
value: 'csv',
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
value: 'json',
|
||||
},
|
||||
{
|
||||
name: 'XLSX',
|
||||
value: 'xlsx',
|
||||
},
|
||||
],
|
||||
default: 'csv',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['questions'],
|
||||
operation: ['resultData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
27
packages/nodes-base/nodes/Metabase/metabase.svg
Normal file
27
packages/nodes-base/nodes/Metabase/metabase.svg
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="256px" height="324px" viewBox="0 0 256 324" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g fill="#509EE3">
|
||||
<ellipse cx="19.3939396" cy="82.7565395" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="19.3939396" cy="137.927566" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="73.6969698" cy="82.7565395" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="73.6969698" cy="138.463513" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="128" cy="82.7565395" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="128" cy="19.703938" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="128" cy="138.463513" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="182.30303" cy="82.7565395" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="236.60606" cy="82.7565395" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="182.30303" cy="138.463513" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="236.60606" cy="138.463513" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="19.3939396" cy="193.098592" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="73.6969698" cy="193.634539" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="128" cy="193.634539" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="182.30303" cy="193.634539" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="236.60606" cy="193.634539" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="19.3939396" cy="248.269618" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="73.6969698" cy="248.805565" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="128" cy="248.805565" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="128" cy="303.976591" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse opacity="0.2" cx="182.30303" cy="248.805565" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
<ellipse cx="236.60606" cy="248.805565" rx="19.3939394" ry="19.703938"></ellipse>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -182,6 +182,7 @@
|
|||
"dist/credentials/MauticOAuth2Api.credentials.js",
|
||||
"dist/credentials/MediumApi.credentials.js",
|
||||
"dist/credentials/MediumOAuth2Api.credentials.js",
|
||||
"dist/credentials/MetabaseApi.credentials.js",
|
||||
"dist/credentials/MessageBirdApi.credentials.js",
|
||||
"dist/credentials/MetabaseApi.credentials.js",
|
||||
"dist/credentials/MicrosoftDynamicsOAuth2Api.credentials.js",
|
||||
|
@ -526,6 +527,7 @@
|
|||
"dist/nodes/Medium/Medium.node.js",
|
||||
"dist/nodes/Merge/Merge.node.js",
|
||||
"dist/nodes/MessageBird/MessageBird.node.js",
|
||||
"dist/nodes/Metabase/Metabase.node.js",
|
||||
"dist/nodes/Microsoft/Dynamics/MicrosoftDynamicsCrm.node.js",
|
||||
"dist/nodes/Microsoft/Excel/MicrosoftExcel.node.js",
|
||||
"dist/nodes/Microsoft/GraphSecurity/MicrosoftGraphSecurity.node.js",
|
||||
|
|
|
@ -1136,6 +1136,7 @@ export interface INodeRequestSend {
|
|||
|
||||
export interface IPostReceiveBase {
|
||||
type: string;
|
||||
enabled?: boolean | string;
|
||||
properties: {
|
||||
[key: string]: string | number | IDataObject;
|
||||
};
|
||||
|
|
|
@ -746,12 +746,34 @@ export class RoutingNode {
|
|||
}
|
||||
|
||||
if (nodeProperties.routing.output.postReceive) {
|
||||
returnData.postReceive.push({
|
||||
data: {
|
||||
parameterValue,
|
||||
},
|
||||
actions: nodeProperties.routing.output.postReceive,
|
||||
const postReceiveActions = nodeProperties.routing.output.postReceive.filter((action) => {
|
||||
if (typeof action === 'function') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof action.enabled === 'string' && action.enabled.charAt(0) === '=') {
|
||||
// If the propertyName is an expression resolve it
|
||||
return this.getParameterValue(
|
||||
action.enabled,
|
||||
itemIndex,
|
||||
runIndex,
|
||||
executeSingleFunctions.getExecuteData(),
|
||||
{ ...additionalKeys, $value: parameterValue },
|
||||
true,
|
||||
) as boolean;
|
||||
}
|
||||
|
||||
return action.enabled !== false;
|
||||
});
|
||||
|
||||
if (postReceiveActions.length) {
|
||||
returnData.postReceive.push({
|
||||
data: {
|
||||
parameterValue,
|
||||
},
|
||||
actions: postReceiveActions,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue