diff --git a/.eslintrc.js b/.eslintrc.js index fec36fa..f9550f1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,108 +28,24 @@ module.exports = { { files: ['package.json'], plugins: ['eslint-plugin-n8n-nodes-base'], - rules: { - 'n8n-nodes-base/community-package-json-author-email-still-default': 'error', - 'n8n-nodes-base/community-package-json-author-missing': 'error', - 'n8n-nodes-base/community-package-json-author-name-missing': 'error', - 'n8n-nodes-base/community-package-json-author-name-still-default': 'error', - 'n8n-nodes-base/community-package-json-description-missing': 'error', - 'n8n-nodes-base/community-package-json-description-still-default': 'error', - 'n8n-nodes-base/community-package-json-keywords-missing': 'error', - 'n8n-nodes-base/community-package-json-keywords-without-official-tag': 'error', - 'n8n-nodes-base/community-package-json-license-missing': 'error', - 'n8n-nodes-base/community-package-json-license-not-default': 'error', - 'n8n-nodes-base/community-package-json-n8n-missing': 'error', - 'n8n-nodes-base/community-package-json-n8n-nodes-empty': 'error', - 'n8n-nodes-base/community-package-json-n8n-nodes-missing': 'error', - 'n8n-nodes-base/community-package-json-name-missing': 'error', - 'n8n-nodes-base/community-package-json-name-still-default': 'error', - 'n8n-nodes-base/community-package-json-repository-url-still-default': 'error', - 'n8n-nodes-base/community-package-json-version-missing': 'error', - }, + extends: ['plugin:n8n-nodes-base/community'], }, { files: ['./credentials/**/*.ts'], plugins: ['eslint-plugin-n8n-nodes-base'], + extends: ['plugin:n8n-nodes-base/credentials'], rules: { - 'n8n-nodes-base/filesystem-wrong-cred-filename': 'error', + 'n8n-nodes-base/cred-class-field-documentation-url-missing': 'off', }, }, { files: ['./nodes/**/*.ts'], plugins: ['eslint-plugin-n8n-nodes-base'], + extends: ['plugin:n8n-nodes-base/nodes'], rules: { - 'n8n-nodes-base/filesystem-wrong-node-filename': 'error', - 'n8n-nodes-base/node-class-description-empty-string': 'error', - 'n8n-nodes-base/node-class-description-icon-not-svg': 'error', - 'n8n-nodes-base/node-class-description-inputs-wrong-trigger-node': 'error', - 'n8n-nodes-base/node-class-description-missing-subtitle': 'error', - 'n8n-nodes-base/node-class-description-outputs-wrong': 'error', - 'n8n-nodes-base/node-execute-block-double-assertion-for-items': 'error', - 'n8n-nodes-base/node-param-collection-type-unsorted-items': 'error', - 'n8n-nodes-base/node-param-default-missing': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-boolean': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-collection': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-fixed-collection': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-multi-options': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-number': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-simplify': 'error', - 'n8n-nodes-base/node-param-default-wrong-for-string': 'error', - 'n8n-nodes-base/node-param-description-boolean-without-whether': 'error', - 'n8n-nodes-base/node-param-description-comma-separated-hyphen': 'error', - 'n8n-nodes-base/node-param-description-empty-string': 'error', - 'n8n-nodes-base/node-param-description-excess-final-period': 'error', - 'n8n-nodes-base/node-param-description-excess-inner-whitespace': 'error', - 'n8n-nodes-base/node-param-description-identical-to-display-name': 'error', - 'n8n-nodes-base/node-param-description-line-break-html-tag': 'error', - 'n8n-nodes-base/node-param-description-lowercase-first-char': 'error', - 'n8n-nodes-base/node-param-description-miscased-id': 'error', - 'n8n-nodes-base/node-param-description-miscased-json': 'error', - 'n8n-nodes-base/node-param-description-miscased-url': 'error', - 'n8n-nodes-base/node-param-description-missing-final-period': 'error', - 'n8n-nodes-base/node-param-description-missing-for-ignore-ssl-issues': 'error', - 'n8n-nodes-base/node-param-description-missing-for-return-all': 'error', - 'n8n-nodes-base/node-param-description-missing-for-simplify': 'error', - 'n8n-nodes-base/node-param-description-missing-from-dynamic-options': 'error', - 'n8n-nodes-base/node-param-description-missing-from-limit': 'error', - 'n8n-nodes-base/node-param-description-unencoded-angle-brackets': 'error', - 'n8n-nodes-base/node-param-description-unneeded-backticks': 'error', - 'n8n-nodes-base/node-param-description-untrimmed': 'error', - 'n8n-nodes-base/node-param-description-url-missing-protocol': 'error', - 'n8n-nodes-base/node-param-description-weak': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-dynamic-multi-options': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-dynamic-options': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-ignore-ssl-issues': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-limit': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-return-all': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-simplify': 'error', - 'n8n-nodes-base/node-param-description-wrong-for-upsert': 'error', - 'n8n-nodes-base/node-param-display-name-excess-inner-whitespace': 'error', - 'n8n-nodes-base/node-param-display-name-miscased': 'error', - 'n8n-nodes-base/node-param-display-name-miscased-id': 'error', - 'n8n-nodes-base/node-param-display-name-untrimmed': 'error', - 'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-multi-options': 'error', - 'n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options': 'error', - 'n8n-nodes-base/node-param-display-name-wrong-for-simplify': 'error', - 'n8n-nodes-base/node-param-display-name-wrong-for-update-fields': 'error', - 'n8n-nodes-base/node-param-min-value-wrong-for-limit': 'error', - 'n8n-nodes-base/node-param-multi-options-type-unsorted-items': 'error', - 'n8n-nodes-base/node-param-operation-without-no-data-expression': 'error', - 'n8n-nodes-base/node-param-option-description-identical-to-name': 'error', - 'n8n-nodes-base/node-param-option-name-containing-star': 'error', - 'n8n-nodes-base/node-param-option-name-duplicate': 'error', - 'n8n-nodes-base/node-param-option-name-wrong-for-get-all': 'error', - 'n8n-nodes-base/node-param-option-name-wrong-for-upsert': 'error', - 'n8n-nodes-base/node-param-option-value-duplicate': 'error', - 'n8n-nodes-base/node-param-options-type-unsorted-items': 'error', - 'n8n-nodes-base/node-param-placeholder-miscased-id': 'error', - 'n8n-nodes-base/node-param-placeholder-missing-email': 'error', - 'n8n-nodes-base/node-param-required-false': 'error', - 'n8n-nodes-base/node-param-resource-with-plural-option': 'error', - 'n8n-nodes-base/node-param-resource-without-no-data-expression': 'error', - 'n8n-nodes-base/node-param-type-options-missing-from-limit': 'error', - 'n8n-nodes-base/node-class-description-inputs-wrong-regular-node': 'error', + 'n8n-nodes-base/node-execute-block-missing-continue-on-fail': 'off', + 'n8n-nodes-base/node-resource-description-filename-against-convention': 'off', + 'n8n-nodes-base/node-param-fixed-collection-type-unsorted-items': 'off', }, }, ], diff --git a/CONTRIBUTOR_LICENSE_AGREEMENT.md b/CONTRIBUTOR_LICENSE_AGREEMENT.md deleted file mode 100644 index 104e263..0000000 --- a/CONTRIBUTOR_LICENSE_AGREEMENT.md +++ /dev/null @@ -1,5 +0,0 @@ -# n8n Contributor License Agreement - -I give n8n permission to license my contributions on any terms they like. I am giving them this license in order to make it possible for them to accept my contributions into their project. - -***As far as the law allows, my contributions come as is, without any warranty or condition, and I will not be liable to anyone for any damages related to this software or this license, under any kind of legal claim.*** diff --git a/LICENSE.md b/LICENSE.md index fa28b97..cb69586 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright 2022 n8n GmbH +Copyright Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/credentials/ExampleCredentials.credentials.ts b/credentials/ExampleCredentialsApi.credentials.ts similarity index 70% rename from credentials/ExampleCredentials.credentials.ts rename to credentials/ExampleCredentialsApi.credentials.ts index fdd9ebf..3d7f059 100644 --- a/credentials/ExampleCredentials.credentials.ts +++ b/credentials/ExampleCredentialsApi.credentials.ts @@ -1,22 +1,27 @@ -import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, NodePropertyTypes } from 'n8n-workflow'; +import { + IAuthenticateGeneric, + ICredentialTestRequest, + ICredentialType, + INodeProperties, +} from 'n8n-workflow'; -export class ExampleCredentials implements ICredentialType { - name = 'exampleCredentials'; - displayName = 'Example Credentials'; - properties = [ +export class ExampleCredentialsApi implements ICredentialType { + name = 'exampleCredentialsApi'; + displayName = 'Example Credentials API'; + properties: INodeProperties[] = [ // The credentials to get from user and save encrypted. // Properties can be defined exactly in the same way // as node properties. { displayName: 'User Name', name: 'username', - type: 'string' as NodePropertyTypes, + type: 'string', default: '', }, { displayName: 'Password', name: 'password', - type: 'string' as NodePropertyTypes, + type: 'string', typeOptions: { password: true, }, @@ -27,7 +32,7 @@ export class ExampleCredentials implements ICredentialType { // This credential is currently not used by any node directly // but the HTTP Request node can use it to make requests. // The credential is also testable due to the `test` property below - authenticate = { + authenticate: IAuthenticateGeneric = { type: 'generic', properties: { auth: { @@ -37,9 +42,9 @@ export class ExampleCredentials implements ICredentialType { qs: { // Send this as part of the query string n8n: 'rocks', - } + }, }, - } as IAuthenticateGeneric; + }; // The block below tells how this credential can be tested test: ICredentialTestRequest = { diff --git a/gulpfile.js b/gulpfile.js index 5cab748..831c707 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,11 +1,16 @@ +const path = require('path'); const { task, src, dest } = require('gulp'); task('build:icons', copyIcons); function copyIcons() { - src('nodes/**/*.{png,svg}').pipe(dest('dist/nodes')); + const nodeSource = path.resolve('nodes', '**', '*.{png,svg}'); + const nodeDestination = path.resolve('dist', 'nodes'); - return src('credentials/**/*.{png,svg}').pipe(dest('dist/credentials')); + src(nodeSource).pipe(dest(nodeDestination)); + + const credSource = path.resolve('credentials', '**', '*.{png,svg}'); + const credDestination = path.resolve('dist', 'credentials'); + + return src(credSource).pipe(dest(credDestination)); } - -// TODO: Add i18n to pipeline diff --git a/nodes/ExampleNode/ExampleNode.node.ts b/nodes/ExampleNode/ExampleNode.node.ts index 29fbcfc..371257b 100644 --- a/nodes/ExampleNode/ExampleNode.node.ts +++ b/nodes/ExampleNode/ExampleNode.node.ts @@ -1,5 +1,10 @@ import { IExecuteFunctions } from 'n8n-core'; -import { INodeExecutionData, INodeType, INodeTypeDescription, NodeOperationError } from 'n8n-workflow'; +import { + INodeExecutionData, + INodeType, + INodeTypeDescription, + NodeOperationError, +} from 'n8n-workflow'; export class ExampleNode implements INodeType { description: INodeTypeDescription = { @@ -10,7 +15,6 @@ export class ExampleNode implements INodeType { description: 'Basic Example Node', defaults: { name: 'Example Node', - color: '#772244', }, inputs: ['main'], outputs: ['main'], @@ -48,11 +52,10 @@ export class ExampleNode implements INodeType { item.json['myString'] = myString; } catch (error) { - // This node should never fail but we want to showcase how // to handle errors. if (this.continueOnFail()) { - items.push({json: this.getInputData(itemIndex)[0].json, error}); + items.push({ json: this.getInputData(itemIndex)[0].json, error }); } else { // Adding `itemIndex` allows other workflows to handle this error if (error.context) { @@ -66,7 +69,6 @@ export class ExampleNode implements INodeType { }); } } - } return this.prepareOutputData(items); diff --git a/nodes/HttpBin/HttpBin.node.ts b/nodes/HttpBin/HttpBin.node.ts index 00eaee7..7fad43d 100644 --- a/nodes/HttpBin/HttpBin.node.ts +++ b/nodes/HttpBin/HttpBin.node.ts @@ -1,11 +1,10 @@ -/* eslint-disable n8n-nodes-base/filesystem-wrong-node-filename */ import { INodeType, INodeTypeDescription } from 'n8n-workflow'; -import { httpVerbFields, httpVerbOperations } from './HttpVerbDescriptions'; +import { httpVerbFields, httpVerbOperations } from './HttpVerbDescription'; export class HttpBin implements INodeType { description: INodeTypeDescription = { displayName: 'HttpBin', - name: 'httpbin', + name: 'httpBin', icon: 'file:httpbin.svg', group: ['transform'], version: 1, @@ -36,11 +35,10 @@ export class HttpBin implements INodeType { * * [Resource & Operation] * - * * https://docs.n8n.io/integrations/creating-nodes/code/create-first-node/#resources-and-operations * - * In our example, the operations are separated into their own file (HTTPVerbDescription) - * to keep this class easy to read + * In our example, the operations are separated into their own file (HTTPVerbDescription.ts) + * to keep this class easy to read. * */ properties: [ @@ -52,10 +50,10 @@ export class HttpBin implements INodeType { options: [ { name: 'HTTP Verb', - value: 'httpverbs', + value: 'httpVerb', }, ], - default: 'httpverbs', + default: 'httpVerb', }, ...httpVerbOperations, diff --git a/nodes/HttpBin/HttpVerbDescriptions.ts b/nodes/HttpBin/HttpVerbDescription.ts similarity index 85% rename from nodes/HttpBin/HttpVerbDescriptions.ts rename to nodes/HttpBin/HttpVerbDescription.ts index 80efc5b..c025130 100644 --- a/nodes/HttpBin/HttpVerbDescriptions.ts +++ b/nodes/HttpBin/HttpVerbDescription.ts @@ -1,21 +1,23 @@ import { INodeProperties } from 'n8n-workflow'; -// This maps the operations to when the Resource option HTTP Verbs is selected +// When the resource `httpVerb` is selected, this `operation` parameter will be shown. export const httpVerbOperations: INodeProperties[] = [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, + displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], }, }, options: [ { name: 'GET', value: 'get', + action: 'Perform a GET request', routing: { request: { method: 'GET', @@ -26,6 +28,7 @@ export const httpVerbOperations: INodeProperties[] = [ { name: 'DELETE', value: 'delete', + action: 'Perform a DELETE request', routing: { request: { method: 'DELETE', @@ -38,8 +41,8 @@ export const httpVerbOperations: INodeProperties[] = [ }, ]; -// Here we define what to show when the GET Operation is selected -// We do that by adding operation: ["get"], to "displayOptions.show" +// Here we define what to show when the `get` operation is selected. +// We do that by adding `operation: ["get"]` to `displayOptions.show` const getOperation: INodeProperties[] = [ { name: 'typeofData', @@ -48,7 +51,7 @@ const getOperation: INodeProperties[] = [ displayName: 'Type of Data', displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], operation: ['get'], }, }, @@ -68,7 +71,7 @@ const getOperation: INodeProperties[] = [ displayName: 'Query Parameters', displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], operation: ['get'], }, }, @@ -109,8 +112,8 @@ const getOperation: INodeProperties[] = [ }, ]; -// Here we define what to show when the DELETE Operation is selected -// We do that by adding operation: ["delete"], to "displayOptions.show" +// Here we define what to show when the DELETE Operation is selected. +// We do that by adding `operation: ["delete"]` to `displayOptions.show` const deleteOperation: INodeProperties[] = [ { name: 'typeofData', @@ -119,7 +122,7 @@ const deleteOperation: INodeProperties[] = [ displayName: 'Type of Data', displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], operation: ['delete'], }, }, @@ -143,7 +146,7 @@ const deleteOperation: INodeProperties[] = [ displayName: 'Query Parameters', displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], operation: ['delete'], typeofData: ['queryParameter'], }, @@ -190,7 +193,7 @@ const deleteOperation: INodeProperties[] = [ displayName: 'JSON Object', displayOptions: { show: { - resource: ['httpverbs'], + resource: ['httpVerb'], operation: ['delete'], typeofData: ['jsonData'], }, @@ -234,12 +237,12 @@ const deleteOperation: INodeProperties[] = [ export const httpVerbFields: INodeProperties[] = [ /* -------------------------------------------------------------------------- */ - /* Http Verbs:Get */ + /* httpVerb:get */ /* -------------------------------------------------------------------------- */ ...getOperation, /* -------------------------------------------------------------------------- */ - /* Http Verbs:Delete */ + /* httpVerb:delete */ /* -------------------------------------------------------------------------- */ ...deleteOperation, ]; diff --git a/package.json b/package.json index fadce8b..ccf505b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "dev": "tsc --watch", "format": "prettier nodes credentials --write", "lint": "tslint -p tsconfig.json -c tslint.json; eslint nodes credentials package.json", - "lintfix": "tslint --fix -p tsconfig.json -c tslint.json; eslint nodes credentials package.json --fix" + "lintfix": "tslint --fix -p tsconfig.json -c tslint.json; eslint nodes credentials package.json --fix", + "prepare": "npm run build && npm run lint" }, "files": [ "dist" @@ -40,10 +41,10 @@ "@types/express": "^4.17.6", "@types/request-promise-native": "~1.0.15", "@typescript-eslint/parser": "^5.29.0", - "eslint-plugin-n8n-nodes-base": "~1.1.1", + "eslint-plugin-n8n-nodes-base": "^1.2.0", "gulp": "^4.0.2", - "n8n-core": "^0.124.0", - "n8n-workflow": "^0.106.0", + "n8n-core": "^0.125.0", + "n8n-workflow": "^0.107.0", "prettier": "^2.7.1", "tslint": "^6.1.2", "typescript": "~4.6.0" diff --git a/tslint.json b/tslint.json index d8dc7ad..f03bbfe 100644 --- a/tslint.json +++ b/tslint.json @@ -111,10 +111,6 @@ "allow-null-check" ], "use-isnan": true, - "quotemark": [ - true, - "single" - ], "quotes": [ "error", "single"