diff --git a/package-lock.json b/package-lock.json index 43b32b3906..80f3a2a61e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13677,15 +13677,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -13721,21 +13712,6 @@ } } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "optional": true - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -13798,58 +13774,6 @@ "worker-rpc": "^0.1.0" } }, - "fork-ts-checker-webpack-plugin-v5": { - "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", - "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", - "optional": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "optional": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -13884,12 +13808,6 @@ "slash": "^2.0.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "optional": true - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -13918,16 +13836,6 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "optional": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -13963,17 +13871,6 @@ } } }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "optional": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -13984,15 +13881,6 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", @@ -14086,12 +13974,6 @@ "requires": { "tslib": "^1.8.1" } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "optional": true } } }, @@ -23570,6 +23452,124 @@ } } }, + "fork-ts-checker-webpack-plugin-v5": { + "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", + "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", + "optional": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "optional": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "optional": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "optional": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "optional": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "optional": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "optional": true + } + } + }, "form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", diff --git a/packages/nodes-base/credentials/MetabaseApi.credentials.ts b/packages/nodes-base/credentials/MetabaseApi.credentials.ts index 8d15fbdeae..f653032979 100644 --- a/packages/nodes-base/credentials/MetabaseApi.credentials.ts +++ b/packages/nodes-base/credentials/MetabaseApi.credentials.ts @@ -32,11 +32,13 @@ export class MetabaseApi implements ICredentialType { }, ]; - authenticate = { - type: 'headerAuth', - properties: { - name: 'Authorization', - value: '=Bearer {{$credentials.accessToken}}', - }, - } as IAuthenticateHeaderAuth; + // ! Special authentication is needed, because the Metabase API is using a special auth + // ! 1st solution would be to authenticate at every request, but this is bad for performance + // ! 2nd solution is to use a implement a special auth function just for Metabase + + // async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise { + // requestOptions.headers!['X-Metabase-Session'] = `${credentials.sessionToken}`; + // return requestOptions; + // } + } diff --git a/packages/nodes-base/nodes/Metabase/AlertsDescription.ts b/packages/nodes-base/nodes/Metabase/AlertsDescription.ts new file mode 100644 index 0000000000..8ebb2dca48 --- /dev/null +++ b/packages/nodes-base/nodes/Metabase/AlertsDescription.ts @@ -0,0 +1,69 @@ +import { + IExecuteSingleFunctions, + IN8nHttpFullResponse, + INodeExecutionData, + INodeProperties, +} from 'n8n-workflow'; + +export const alertsOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'alerts', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all the alerts', + routing: { + request: { + method: 'GET', + url: '/api/alert/', + }, + }, + }, + { + name: 'Get Alert', + value: 'getAlert', + description: 'Get specific alert', + routing: { + request: { + method: 'GET', + url: '={{"/api/alert/" + $parameter.alertId}}', + returnFullResponse: true, + }, + }, + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +]; + +export const alertsFields: INodeProperties[] = [ + { + displayName: 'Alert Id', + name: 'alertId', + type: 'string', + required: true, + placeholder: '0', + displayOptions: { + show: { + resource: [ + 'alerts', + ], + operation: [ + 'getAlert', + ], + }, + }, + default: '', + }, +]; diff --git a/packages/nodes-base/nodes/Metabase/DatabasesDescription.ts b/packages/nodes-base/nodes/Metabase/DatabasesDescription.ts new file mode 100644 index 0000000000..cb1df04f98 --- /dev/null +++ b/packages/nodes-base/nodes/Metabase/DatabasesDescription.ts @@ -0,0 +1,69 @@ +import { + IExecuteSingleFunctions, + IN8nHttpFullResponse, + INodeExecutionData, + INodeProperties, +} from 'n8n-workflow'; + +export const databasesOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'databases', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all the databases', + routing: { + request: { + method: 'GET', + url: '/api/database/', + }, + }, + }, + { + name: 'Get Fields', + value: 'getFields', + description: 'Get fields from database', + routing: { + request: { + method: 'GET', + url: '={{"/api/databse/" + $parameter.databaseId} + "/fields"}', + returnFullResponse: true, + }, + }, + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +]; + +export const databasesFields: INodeProperties[] = [ + { + displayName: 'Database Id', + name: 'databaseId', + type: 'string', + required: true, + placeholder: '0', + displayOptions: { + show: { + resource: [ + 'databases', + ], + operation: [ + 'getFields', + ], + }, + }, + default: '', + }, +]; diff --git a/packages/nodes-base/nodes/Metabase/Metabase.node.ts b/packages/nodes-base/nodes/Metabase/Metabase.node.ts index 5ded77abea..6c08c151c2 100644 --- a/packages/nodes-base/nodes/Metabase/Metabase.node.ts +++ b/packages/nodes-base/nodes/Metabase/Metabase.node.ts @@ -1,9 +1,28 @@ 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', @@ -14,14 +33,14 @@ export class Metabase implements INodeType { subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Use the Metabase API', defaults: { - name: 'Metabse', + name: 'Metabase', color: '#ff0000', }, inputs: ['main'], outputs: ['main'], credentials: [ { - name: 'MetabseApi', + name: 'metabaseApi', required: true, testedBy: { request: { @@ -33,10 +52,8 @@ export class Metabase implements INodeType { ], requestDefaults: { returnFullResponse: true, - baseURL: '=${{credentials.url}}', - + baseURL: '={{$credentials.url}}', headers: { - 'developer-token': '={{$credentials.developerToken}}', }, }, properties: [ @@ -65,6 +82,14 @@ export class Metabase implements INodeType { default: 'questions', description: 'The resource to operate on.', }, + ...questionsOperations, + ...questionsFields, + ...metricsOperations, + ...metricsFields, + ...databasesOperations, + ...databasesFields, + ...alertsOperations, + ...alertsFields, ], }; } diff --git a/packages/nodes-base/nodes/Metabase/MetricsDescription.ts b/packages/nodes-base/nodes/Metabase/MetricsDescription.ts new file mode 100644 index 0000000000..4bcdc45be8 --- /dev/null +++ b/packages/nodes-base/nodes/Metabase/MetricsDescription.ts @@ -0,0 +1,69 @@ +import { + IExecuteSingleFunctions, + IN8nHttpFullResponse, + INodeExecutionData, + INodeProperties, +} from 'n8n-workflow'; + +export const metricsOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'metrics', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all the metrics', + routing: { + request: { + method: 'GET', + url: '/api/metric/', + }, + }, + }, + { + name: 'Get', + value: 'get', + description: 'Get a specific metric', + routing: { + request: { + method: 'GET', + url: '={{"/api/metric/" + $parameter.metricId}}', + returnFullResponse: true, + }, + }, + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +]; + +export const metricsFields: INodeProperties[] = [ + { + displayName: 'Metric Id', + name: 'metricId', + type: 'string', + required: true, + placeholder: '0', + displayOptions: { + show: { + resource: [ + 'metrics', + ], + operation: [ + 'get', + ], + }, + }, + default: '', + }, +]; diff --git a/packages/nodes-base/nodes/Metabase/QuestionsDescription.ts b/packages/nodes-base/nodes/Metabase/QuestionsDescription.ts new file mode 100644 index 0000000000..5a0517a8f5 --- /dev/null +++ b/packages/nodes-base/nodes/Metabase/QuestionsDescription.ts @@ -0,0 +1,117 @@ +import { + IExecuteSingleFunctions, + IN8nHttpFullResponse, + INodeExecutionData, + INodeProperties, +} from 'n8n-workflow'; + +export const questionsOperations: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'questions', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all the questions', + routing: { + request: { + method: 'GET', + url: '/api/card/', + }, + }, + }, + { + name: 'Get', + value: 'get', + description: 'Get a specific question', + routing: { + request: { + method: 'GET', + url: '={{"/api/card/" + $parameter.questionId}}', + returnFullResponse: true, + }, + }, + }, + { + name: 'Export', + value: 'export', + description: 'Export question to a specific file format', + routing: { + request: { + method: 'POST', + url: '={{"/api/card/" + $parameter.questionId + "/query/" + $parameter.format}}', + returnFullResponse: true, + }, + }, + }, + ], + default: 'getAll', + description: 'The operation to perform.', + }, +]; + +export const questionsFields: INodeProperties[] = [ + { + displayName: 'Question Id', + name: 'questionId', + type: 'string', + required: true, + placeholder: '0', + displayOptions: { + show: { + resource: [ + 'questions', + ], + operation: [ + 'get', + 'export', + ], + }, + }, + default: '', + }, + { + displayName: 'Format', + name: 'format', + type: 'options', + required: true, + options: [ + { + name: 'CSV', + value: 'csv', + }, + { + name: 'JSON', + value: 'json', + }, + { + name: 'API', + value: 'api', + }, + { + name: 'XLSX', + value: 'xlsx', + }, + ], + default: 'csv', + displayOptions: { + show: { + resource: [ + 'questions', + ], + operation: [ + 'export', + ], + }, + }, + }, +];