Merge pull request #13 from n8n-io/n8n-3857-add-example-node-to-community-repo

Add Example HttpBin Node
This commit is contained in:
Omar Ajoue 2022-06-21 11:26:25 +02:00 committed by GitHub
commit 102187b56e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 551 additions and 21 deletions

2
.eslintignore Normal file
View file

@ -0,0 +1,2 @@
packages/editor-ui
packages/design-system

106
.eslintrc.js Normal file
View file

@ -0,0 +1,106 @@
module.exports = {
root: true,
env: {
browser: true,
es6: true,
node: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
project: ['./tsconfig.json'],
sourceType: 'module',
},
ignorePatterns: [
'.eslintrc.js',
'**/*.js',
'**/node_modules/**',
'**/dist/**',
'**/test/**',
'**/templates/**',
'**/ormconfig.ts',
'**/migrations/**',
],
overrides: [
{
files: [ './**/*.ts' ],
plugins: ['eslint-plugin-n8n-nodes-base'],
rules: {
'n8n-nodes-base/filesystem-wrong-cred-filename': 'error',
'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',
},
},
],
};

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ dist
npm-debug.log* npm-debug.log*
package-lock.json package-lock.json
yarn.lock yarn.lock
.vscode/launch.json

View file

@ -13,6 +13,8 @@ All nodes are npm packages. To make your custom node available to the community,
2. Open the project in your editor. 2. Open the project in your editor.
3. Browse the examples in `/nodes`. Modify the examples, or replace them with your own nodes. 3. Browse the examples in `/nodes`. Modify the examples, or replace them with your own nodes.
4. Update the `package.json` to match your details. 4. Update the `package.json` to match your details.
5. Run `npm run lint` to check for errors or `npm run lintfix` to automatically fix errors when possible.
6. Publish your package to npm. More information on the links below.
## More information ## More information

View file

@ -0,0 +1,60 @@
import {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
export class HttpBinApi implements ICredentialType {
name = 'httpbinApi';
displayName = 'HttpBin API';
documentationUrl = 'httpbin';
properties: INodeProperties[] = [
{
displayName: 'Token',
name: 'token',
type: 'string',
default: '',
},
// {
// displayName: "API Key",
// name: "apiKey",
// type: "string",
// default: "",
// },
{
displayName: 'Domain',
name: 'domain',
type: 'string',
default: 'https://httpbin.org',
},
];
// authenticate = {
// type: "headerAuth",
// properties: {
// name: "api-key",
// value: "={{$credentials.apiKey}}",
// },
// } as IAuthenticateHeaderAuth;
authenticate = async (
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> => {
const headers = requestOptions.headers || {};
const authentication = { Authorization: `Bearer ${credentials.token}` };
Object.assign(requestOptions, {
headers: { ...authentication, ...headers },
});
return requestOptions;
};
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials?.domain}}',
url: '/bearer',
},
};
}

View file

@ -0,0 +1,21 @@
{
"node": "n8n-nodes-base.httpbin",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": [
"Development",
"Developer Tools"
],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/credentials/httpbin"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.httpbin/"
}
]
}
}

View file

@ -0,0 +1,65 @@
/* eslint-disable n8n-nodes-base/filesystem-wrong-node-filename */
import { INodeType, INodeTypeDescription } from 'n8n-workflow';
import { httpVerbFields, httpVerbOperations } from './HttpVerbDescriptions';
export class HttpBin implements INodeType {
description: INodeTypeDescription = {
displayName: 'HttpBin',
name: 'httpbin',
icon: 'file:httpbin.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Interact with HttpBin API',
defaults: {
name: 'HttpBin',
color: '#3b4151',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'httpbinApi',
required: false,
},
],
requestDefaults: {
baseURL: 'https://httpbin.org',
url: '',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
},
/**
* In the properties array we have two mandatory options objects required
*
* [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
*
*/
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'HTTP Verb',
value: 'httpverbs',
},
],
default: 'httpverbs',
},
...httpVerbOperations,
...httpVerbFields,
],
};
}

View file

@ -0,0 +1,246 @@
import { INodeProperties } from 'n8n-workflow';
// This maps the operations to when the Resource option HTTP Verbs is selected
export const httpVerbOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['httpverbs'],
},
},
options: [
{
name: 'GET',
value: 'get',
routing: {
request: {
method: 'GET',
url: '/get',
},
},
},
{
name: 'DELETE',
value: 'delete',
routing: {
request: {
method: 'DELETE',
url: '/delete',
},
},
},
],
default: 'get',
},
];
// 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',
default: 'queryParameter',
description: 'Select type of data to send [Query Parameters]',
displayName: 'Type of Data',
displayOptions: {
show: {
resource: ['httpverbs'],
operation: ['get'],
},
},
type: 'options',
options: [
{
name: 'Query',
value: 'queryParameter',
},
],
required: true,
},
{
name: 'arguments',
default: {},
description: 'The request\'s query parameters',
displayName: 'Query Parameters',
displayOptions: {
show: {
resource: ['httpverbs'],
operation: ['get'],
},
},
options: [
{
name: 'keyvalue',
displayName: 'Key:Value',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
required: true,
description: 'Key of query parameter',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
routing: {
send: {
property: '={{$parent.key}}',
type: 'query',
},
},
required: true,
description: 'Value of query parameter',
},
],
},
],
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
},
];
// 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',
default: 'queryParameter',
description:
'Select type of data to send [Query Parameter Arguments, JSON-Body]',
displayName: 'Type of Data',
displayOptions: {
show: {
resource: ['httpverbs'],
operation: ['delete'],
},
},
options: [
{
name: 'Query',
value: 'queryParameter',
},
{
name: 'JSON',
value: 'jsonData',
},
],
required: true,
type: 'options',
},
{
name: 'arguments',
default: {},
description: 'The request\'s query parameters',
displayName: 'Query Parameters',
displayOptions: {
show: {
resource: ['httpverbs'],
operation: ['delete'],
typeofData: ['queryParameter'],
},
},
options: [
{
name: 'keyvalue',
displayName: 'Key:Value',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
required: true,
description: 'Key of query parameter',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
routing: {
send: {
property: '={{$parent.key}}',
type: 'query',
},
},
required: true,
description: 'Value of query parameter',
},
],
},
],
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
},
{
name: 'arguments',
default: {},
description: 'The request\'s JSON properties',
displayName: 'JSON Object',
displayOptions: {
show: {
resource: ['httpverbs'],
operation: ['delete'],
typeofData: ['jsonData'],
},
},
options: [
{
name: 'keyvalue',
displayName: 'Key:Value',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
required: true,
description: 'Key of JSON property',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
routing: {
send: {
property: '={{$parent.key}}',
type: 'body',
},
},
required: true,
description: 'Value of JSON property',
},
],
},
],
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
},
];
export const httpVerbFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* Http Verbs:Get */
/* -------------------------------------------------------------------------- */
...getOperation,
/* -------------------------------------------------------------------------- */
/* Http Verbs:Delete */
/* -------------------------------------------------------------------------- */
...deleteOperation,
];

18
nodes/HttpBin/httpbin.svg Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve"> <image id="image0" width="32" height="32" x="0" y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElN
RQfmBg4UAC/TqOZZAAACA0lEQVRIx5XVv09TURwF8M+jFHDSyRkGFhPAEfyRdDHi5uriXyDoYgKT
MJDWzUT/Ahf/AiOEpajEgCESmpiYmDCxGowDTYE+h76+vte+15Zzk753b7733HNO772PbEw7ECba
genswtEcgl0/PHARV72066YrIDSZ6k8KBym4741r0XsB284TdUX8chn1zrzwJUmw4KFXPqjFE0Y0
u5YKEhpmfLZuy7f2wLKGI8WhDRYdaVhurdTCidmU5P44N+skaaGQH1IfFFrOYMotT932zNgQExve
OfTeT8dtBceO3TFlOyopY7UPxV+/fWyn3Y0xrFhJjZWFXhs12pKdRO9ObGSuyB8Xbd9JjMjDc6HQ
IcrKqAiVe8vyCEJPrGBWxZYqqtZt9RbmHabAvAAVdVUlJTvWshbMt0AYn40OmlchSKOePTyYIMQn
rb8yI8TsDCrRs4od7Jv3KOoPGWKboBqp2LN3FQvdO7EPshSsRSTXrSop2cSiiUGkG/bj2JqaQiHW
4nv50mFcu28j30KQarAnEPhuzvwwGYQ975vx7+JwGXTjTIAzoYlhCArR5d0KkfauqJAVY6+FG5hD
OS6veqyCuSiTAQT/jKmlQtyxIBCoZV28HQvN6LuQvJFC4xjvibfYOZUdUXd9taTWJbOubiIVXmjG
W/fs9qpZcpr6pOe1U0udSf8BR7ef4yxyOskAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjItMDYtMTRU
MTc6MDA6NDcrMDM6MDBfo1sRAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIyLTA2LTE0VDE3OjAwOjQ3
KzAzOjAwLv7jrQAAAABJRU5ErkJggg==" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -19,9 +19,8 @@
"scripts": { "scripts": {
"dev": "npm run watch", "dev": "npm run watch",
"build": "tsc && gulp", "build": "tsc && gulp",
"lint": "tslint -p tsconfig.json -c tslint.json", "lint": "tslint -p tsconfig.json -c tslint.json && node_modules/eslint/bin/eslint.js ./nodes",
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json", "lintfix": "tslint --fix -p tsconfig.json -c tslint.json && node_modules/eslint/bin/eslint.js --fix ./nodes",
"nodelinter": "nodelinter",
"watch": "tsc --watch", "watch": "tsc --watch",
"test": "jest" "test": "jest"
}, },
@ -30,10 +29,12 @@
], ],
"n8n": { "n8n": {
"credentials": [ "credentials": [
"dist/credentials/ExampleCredentials.credentials.js" "dist/credentials/ExampleCredentials.credentials.js",
"dist/credentials/HttpBinApi.credentials.js"
], ],
"nodes": [ "nodes": [
"dist/nodes/ExampleNode/ExampleNode.node.js" "dist/nodes/ExampleNode/ExampleNode.node.js",
"dist/nodes/HttpBin/HttpBin.node.js"
] ]
}, },
"devDependencies": { "devDependencies": {
@ -41,10 +42,11 @@
"@types/jest": "^26.0.13", "@types/jest": "^26.0.13",
"@types/node": "^14.17.27", "@types/node": "^14.17.27",
"@types/request-promise-native": "~1.0.15", "@types/request-promise-native": "~1.0.15",
"@typescript-eslint/parser": "^5.29.0",
"eslint-plugin-n8n-nodes-base": "^1.0.43",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"jest": "^26.4.2", "jest": "^26.4.2",
"n8n-workflow": "~0.104.0", "n8n-workflow": "~0.104.0",
"nodelinter": "^0.1.9",
"ts-jest": "^26.3.0", "ts-jest": "^26.3.0",
"tslint": "^6.1.2", "tslint": "^6.1.2",
"typescript": "~4.3.5" "typescript": "~4.3.5"

View file

@ -17,7 +17,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"declaration": true, "declaration": true,
"outDir": "./dist/", "outDir": "./dist/",
"target": "es2017", "target": "es2019",
"sourceMap": true, "sourceMap": true,
"esModuleInterop": true "esModuleInterop": true
}, },

View file

@ -46,7 +46,11 @@
"forin": true, "forin": true,
"jsdoc-format": true, "jsdoc-format": true,
"label-position": true, "label-position": true,
"indent": [true, "tabs", 2], "indent": [
true,
"tabs",
2
],
"member-access": [ "member-access": [
true, true,
"no-public" "no-public"
@ -61,10 +65,13 @@
"no-default-export": true, "no-default-export": true,
"no-duplicate-variable": true, "no-duplicate-variable": true,
"no-inferrable-types": true, "no-inferrable-types": true,
"ordered-imports": [true, { "ordered-imports": [
"import-sources-order": "any", true,
"named-imports-order": "case-insensitive" {
}], "import-sources-order": "any",
"named-imports-order": "case-insensitive"
}
],
"no-namespace": [ "no-namespace": [
true, true,
"allow-declarations" "allow-declarations"
@ -90,13 +97,13 @@
"trailing-comma": [ "trailing-comma": [
true, true,
{ {
"multiline": { "multiline": {
"objects": "always", "objects": "always",
"arrays": "always", "arrays": "always",
"functions": "always", "functions": "always",
"typeLiterals": "ignore" "typeLiterals": "ignore"
}, },
"esSpecCompliant": true "esSpecCompliant": true
} }
], ],
"triple-equals": [ "triple-equals": [