Merge branch 'master' of github.com:n8n-io/n8n into N8N-2563-workflow-activation

This commit is contained in:
Mutasem 2022-01-17 11:05:11 +01:00
commit 1de1b1cbe7
28 changed files with 1435 additions and 774 deletions

View file

@ -87,7 +87,7 @@ npm install -g lerna
### Actual n8n setup ### Actual n8n setup
> **IMPORTANT**: All the steps bellow have to get executed at least once to get the development setup up and running! > **IMPORTANT**: All the steps below have to get executed at least once to get the development setup up and running!
Now that everything n8n requires to run is installed the actual n8n code can be Now that everything n8n requires to run is installed the actual n8n code can be
checked out and set up: checked out and set up:

1082
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,12 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { Command, flags } from '@oclif/command'; import { Command, flags } from '@oclif/command';
import { BinaryDataManager, IBinaryDataConfig, UserSettings } from 'n8n-core'; import {
BinaryDataManager,
IBinaryDataConfig,
UserSettings,
PLACEHOLDER_EMPTY_WORKFLOW_ID,
} from 'n8n-core';
import { INode, LoggerProxy } from 'n8n-workflow'; import { INode, LoggerProxy } from 'n8n-workflow';
import { import {
@ -96,8 +101,8 @@ export class Execute extends Command {
console.info(`The file "${flags.file}" does not contain valid workflow data.`); console.info(`The file "${flags.file}" does not contain valid workflow data.`);
return; return;
} }
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
workflowId = workflowData.id!.toString(); workflowId = workflowData.id ? workflowData.id.toString() : PLACEHOLDER_EMPTY_WORKFLOW_ID;
} }
// Wait till the database is ready // Wait till the database is ready

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n", "name": "n8n",
"version": "0.158.0", "version": "0.159.0",
"description": "n8n Workflow Automation Tool", "description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",
@ -111,10 +111,10 @@
"localtunnel": "^2.0.0", "localtunnel": "^2.0.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"mysql2": "~2.3.0", "mysql2": "~2.3.0",
"n8n-core": "~0.100.0", "n8n-core": "~0.101.0",
"n8n-editor-ui": "~0.125.0", "n8n-editor-ui": "~0.126.0",
"n8n-nodes-base": "~0.156.0", "n8n-nodes-base": "~0.157.0",
"n8n-workflow": "~0.82.0", "n8n-workflow": "~0.83.0",
"oauth-1.0a": "^2.2.6", "oauth-1.0a": "^2.2.6",
"open": "^7.0.0", "open": "^7.0.0",
"pg": "^8.3.0", "pg": "^8.3.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n-core", "name": "n8n-core",
"version": "0.100.0", "version": "0.101.0",
"description": "Core functionality of n8n", "description": "Core functionality of n8n",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",
@ -51,7 +51,7 @@
"form-data": "^4.0.0", "form-data": "^4.0.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"n8n-workflow": "~0.82.0", "n8n-workflow": "~0.83.0",
"oauth-1.0a": "^2.2.6", "oauth-1.0a": "^2.2.6",
"p-cancelable": "^2.0.0", "p-cancelable": "^2.0.0",
"qs": "^6.10.1", "qs": "^6.10.1",

View file

@ -6,5 +6,6 @@ export const USER_FOLDER_ENV_OVERWRITE = 'N8N_USER_FOLDER';
export const USER_SETTINGS_FILE_NAME = 'config'; export const USER_SETTINGS_FILE_NAME = 'config';
export const USER_SETTINGS_SUBFOLDER = '.n8n'; export const USER_SETTINGS_SUBFOLDER = '.n8n';
export const PLACEHOLDER_EMPTY_EXECUTION_ID = '__UNKOWN__'; export const PLACEHOLDER_EMPTY_EXECUTION_ID = '__UNKOWN__';
export const PLACEHOLDER_EMPTY_WORKFLOW_ID = '__EMPTY__';
export const TUNNEL_SUBDOMAIN_ENV = 'N8N_TUNNEL_SUBDOMAIN'; export const TUNNEL_SUBDOMAIN_ENV = 'N8N_TUNNEL_SUBDOMAIN';
export const WAIT_TIME_UNLIMITED = '3000-01-01T00:00:00.000Z'; export const WAIT_TIME_UNLIMITED = '3000-01-01T00:00:00.000Z';

View file

@ -8,9 +8,7 @@
"jest" "jest"
], ],
"module": "commonjs", "module": "commonjs",
"noImplicitAny": true,
"removeComments": true, "removeComments": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"strict": true, "strict": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n-editor-ui", "name": "n8n-editor-ui",
"version": "0.125.0", "version": "0.126.0",
"description": "Workflow Editor UI for n8n", "description": "Workflow Editor UI for n8n",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",
@ -75,7 +75,7 @@
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.set": "^4.3.2", "lodash.set": "^4.3.2",
"n8n-workflow": "~0.82.0", "n8n-workflow": "~0.83.0",
"monaco-editor-webpack-plugin": "^5.0.0", "monaco-editor-webpack-plugin": "^5.0.0",
"normalize-wheel": "^1.0.1", "normalize-wheel": "^1.0.1",
"prismjs": "^1.17.1", "prismjs": "^1.17.1",

View file

@ -72,7 +72,7 @@ The base text file for each locale is located at `/packages/editor-ui/src/plugin
cp ./packages/editor-ui/src/plugins/i18n/locales/en.json ./packages/editor-ui/src/plugins/i18n/locales/de.json cp ./packages/editor-ui/src/plugins/i18n/locales/en.json ./packages/editor-ui/src/plugins/i18n/locales/de.json
``` ```
2. Find in the UI a string to translate, and search for it in the newly created base text file. Alternatively,find in `/editor-ui` a call to `$locale.baseText(key)`, e.g. `$locale.baseText('workflowActivator.deactivateWorkflow')`, and take note of the key and find it in the newly created base text file. 2. Find in the UI a string to translate, and search for it in the newly created base text file. Alternatively, find in `/editor-ui` a call to `$locale.baseText(key)`, e.g. `$locale.baseText('workflowActivator.deactivateWorkflow')`, and take note of the key and find it in the newly created base text file.
> **Note**: If you cannot find a string in the new base text file, either it does not belong to base text (i.e., the string might be part of header text, credential text, or node text), or the string might belong to the backend, where i18n is currently unsupported. > **Note**: If you cannot find a string in the new base text file, either it does not belong to base text (i.e., the string might be part of header text, credential text, or node text), or the string might belong to the backend, where i18n is currently unsupported.
@ -99,8 +99,8 @@ A credential translation file is placed at `/nodes-base/credentials/translations
credentials credentials
└── translations └── translations
└── de └── de
├── githubApi.json ├── githubApi.json
└── githubOAuth2Api.json └── githubOAuth2Api.json
``` ```
Every credential must have its own credential translation file. Every credential must have its own credential translation file.
@ -124,8 +124,8 @@ GitHub
├── GitHubTrigger.node.ts ├── GitHubTrigger.node.ts
└── translations └── translations
└── de └── de
├── github.json ├── github.json
└── githubTrigger.json └── githubTrigger.json
``` ```
Every node must have its own node translation file. Every node must have its own node translation file.
@ -180,14 +180,6 @@ export class GithubApi implements ICredentialType {
} }
``` ```
```json
{
"server": {...},
"user": {...},
"accessToken": {...},
}
```
The object for each node credential parameter allows for the keys `displayName`, `description`, and `placeholder`. The object for each node credential parameter allows for the keys `displayName`, `description`, and `placeholder`.
```json ```json
@ -215,14 +207,7 @@ Only existing parameters are translatable. If a credential parameter does not ha
> **Note**: All keys are optional. Missing translations trigger a fallback to the `en` locale strings. > **Note**: All keys are optional. Missing translations trigger a fallback to the `en` locale strings.
Each node translation file is an object that allows for two keys, `header` and `nodeView`, which are the _sections_ of each node translation: Each node translation file is an object that allows for two keys, `header` and `nodeView`, which are the _sections_ of each node translation.
```json
{
"header": { ... },
"nodeView": { ... },
}
```
The `header` section points to an object that may contain only two keys, `displayName` and `description`, matching the node's `description.displayName` and `description.description`. The `header` section points to an object that may contain only two keys, `displayName` and `description`, matching the node's `description.displayName` and `description.description`.
@ -280,7 +265,9 @@ export class Github implements INodeType {
```json ```json
{ {
"nodeView": { "nodeView": {
"resource": {...}, "resource": {
"displayName": "🇩🇪 Resource",
},
}, },
} }
``` ```

View file

@ -2,9 +2,7 @@
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"module": "esnext", "module": "esnext",
"noImplicitAny": true,
"removeComments": true, "removeComments": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"strict": true, "strict": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n-node-dev", "name": "n8n-node-dev",
"version": "0.39.0", "version": "0.40.0",
"description": "CLI to simplify n8n credentials/node development", "description": "CLI to simplify n8n credentials/node development",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",
@ -61,8 +61,8 @@
"change-case": "^4.1.1", "change-case": "^4.1.1",
"copyfiles": "^2.1.1", "copyfiles": "^2.1.1",
"inquirer": "^7.0.1", "inquirer": "^7.0.1",
"n8n-core": "~0.100.0", "n8n-core": "~0.101.0",
"n8n-workflow": "~0.82.0", "n8n-workflow": "~0.83.0",
"oauth-1.0a": "^2.2.6", "oauth-1.0a": "^2.2.6",
"replace-in-file": "^6.0.0", "replace-in-file": "^6.0.0",
"request": "^2.88.2", "request": "^2.88.2",

View file

@ -8,9 +8,7 @@
], ],
"module": "commonjs", "module": "commonjs",
"esModuleInterop": true, "esModuleInterop": true,
"noImplicitAny": true,
"removeComments": true, "removeComments": true,
"strictNullChecks": true,
"strict": true, "strict": true,
"preserveConstEnums": true, "preserveConstEnums": true,
"declaration": true, "declaration": true,

View file

@ -0,0 +1,30 @@
import {
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
export class JenkinsApi implements ICredentialType {
name = 'jenkinsApi';
displayName = 'Jenkins API';
documentationUrl = 'jenkins';
properties: INodeProperties[] = [
{
displayName: 'Jenking Username',
name: 'username',
type: 'string',
default: '',
},
{
displayName: 'Personal API Token',
name: 'apiKey',
type: 'string',
default: '',
},
{
displayName: 'Jenkins Instance URL',
name: 'baseUrl',
type: 'string',
default: '',
},
];
}

View file

@ -130,7 +130,7 @@ export class Box implements INodeType {
let mimeType: string | undefined; let mimeType: string | undefined;
responseData = await boxApiRequest.call(this, 'GET', `/files/${fileId}/content`, {}, {}, undefined, { resolveWithFullResponse: true }); responseData = await boxApiRequest.call(this, 'GET', `/files/${fileId}/content`, {}, {}, undefined, { encoding: null, resolveWithFullResponse: true });
const newItem: INodeExecutionData = { const newItem: INodeExecutionData = {
json: items[i].json, json: items[i].json,

View file

@ -25,6 +25,28 @@ export class GraphQL implements INodeType {
inputs: ['main'], inputs: ['main'],
outputs: ['main'], outputs: ['main'],
credentials: [ credentials: [
{
name: 'httpBasicAuth',
required: true,
displayOptions: {
show: {
authentication: [
'basicAuth',
],
},
},
},
{
name: 'httpDigestAuth',
required: true,
displayOptions: {
show: {
authentication: [
'digestAuth',
],
},
},
},
{ {
name: 'httpHeaderAuth', name: 'httpHeaderAuth',
required: true, required: true,
@ -36,6 +58,39 @@ export class GraphQL implements INodeType {
}, },
}, },
}, },
{
name: 'httpQueryAuth',
required: true,
displayOptions: {
show: {
authentication: [
'queryAuth',
],
},
},
},
{
name: 'oAuth1Api',
required: true,
displayOptions: {
show: {
authentication: [
'oAuth1',
],
},
},
},
{
name: 'oAuth2Api',
required: true,
displayOptions: {
show: {
authentication: [
'oAuth2',
],
},
},
},
], ],
properties: [ properties: [
{ {
@ -43,10 +98,30 @@ export class GraphQL implements INodeType {
name: 'authentication', name: 'authentication',
type: 'options', type: 'options',
options: [ options: [
{
name: 'Basic Auth',
value: 'basicAuth',
},
{
name: 'Digest Auth',
value: 'digestAuth',
},
{ {
name: 'Header Auth', name: 'Header Auth',
value: 'headerAuth', value: 'headerAuth',
}, },
{
name: 'Query Auth',
value: 'queryAuth',
},
{
name: 'OAuth1',
value: 'oAuth1',
},
{
name: 'OAuth2',
value: 'oAuth2',
},
{ {
name: 'None', name: 'None',
value: 'none', value: 'none',
@ -229,7 +304,12 @@ export class GraphQL implements INodeType {
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData(); const items = this.getInputData();
const httpBasicAuth = await this.getCredentials('httpBasicAuth');
const httpDigestAuth = await this.getCredentials('httpDigestAuth');
const httpHeaderAuth = await this.getCredentials('httpHeaderAuth'); const httpHeaderAuth = await this.getCredentials('httpHeaderAuth');
const httpQueryAuth = await this.getCredentials('httpQueryAuth');
const oAuth1Api = await this.getCredentials('oAuth1Api');
const oAuth2Api = await this.getCredentials('oAuth2Api');
let requestOptions: OptionsWithUri & RequestPromiseOptions; let requestOptions: OptionsWithUri & RequestPromiseOptions;
@ -260,15 +340,35 @@ export class GraphQL implements INodeType {
}; };
// Add credentials if any are set // Add credentials if any are set
if (httpBasicAuth !== undefined) {
requestOptions.auth = {
user: httpBasicAuth.user as string,
pass: httpBasicAuth.password as string,
};
}
if (httpHeaderAuth !== undefined) { if (httpHeaderAuth !== undefined) {
requestOptions.headers![httpHeaderAuth.name as string] = httpHeaderAuth.value; requestOptions.headers![httpHeaderAuth.name as string] = httpHeaderAuth.value;
} }
if (httpQueryAuth !== undefined) {
if (!requestOptions.qs) {
requestOptions.qs = {};
}
requestOptions.qs![httpQueryAuth.name as string] = httpQueryAuth.value;
}
if (httpDigestAuth !== undefined) {
requestOptions.auth = {
user: httpDigestAuth.user as string,
pass: httpDigestAuth.password as string,
sendImmediately: false,
};
}
const gqlQuery = this.getNodeParameter('query', itemIndex, '') as string; const gqlQuery = this.getNodeParameter('query', itemIndex, '') as string;
if (requestMethod === 'GET') { if (requestMethod === 'GET') {
requestOptions.qs = { if (!requestOptions.qs) {
query: gqlQuery, requestOptions.qs = {};
}; }
requestOptions.qs.query = gqlQuery;
} else { } else {
if (requestFormat === 'json') { if (requestFormat === 'json') {
requestOptions.body = { requestOptions.body = {
@ -292,7 +392,15 @@ export class GraphQL implements INodeType {
} }
} }
const response = await this.helpers.request(requestOptions); let response;
// Now that the options are all set make the actual http request
if (oAuth1Api !== undefined) {
response = await this.helpers.requestOAuth1.call(this, 'oAuth1Api', requestOptions);
} else if (oAuth2Api !== undefined) {
response = await this.helpers.requestOAuth2.call(this, 'oAuth2Api', requestOptions, { tokenType: 'Bearer' });
} else {
response = await this.helpers.request(requestOptions);
}
if (responseFormat === 'string') { if (responseFormat === 'string') {
const dataPropertyName = this.getNodeParameter('dataPropertyName', 0) as string; const dataPropertyName = this.getNodeParameter('dataPropertyName', 0) as string;

View file

@ -0,0 +1,45 @@
import {
OptionsWithUri,
} from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject,
NodeApiError,
} from 'n8n-workflow';
export async function jenkinsApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, uri: string, qs: IDataObject = {}, body: any = '', option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = await this.getCredentials('jenkinsApi') as IDataObject;
let options: OptionsWithUri = {
headers: {
'Accept': 'application/json',
},
method,
auth: {
username: credentials.username as string,
password: credentials.apiKey as string,
},
uri: `${tolerateTrailingSlash(credentials.baseUrl as string)}${uri}`,
json: true,
qs,
body,
};
options = Object.assign({}, options, option);
try {
return await this.helpers.request!(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error);
}
}
export function tolerateTrailingSlash(baseUrl: string) {
return baseUrl.endsWith('/')
? baseUrl.substr(0, baseUrl.length - 1)
: baseUrl;
}

View file

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

View file

@ -0,0 +1,682 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
NodeApiError,
NodeCredentialTestResult,
} from 'n8n-workflow';
import {
jenkinsApiRequest,
tolerateTrailingSlash
} from './GenericFunctions';
export type JenkinsApiCredentials = {
username: string;
apiKey: string;
baseUrl: string;
};
export class Jenkins implements INodeType {
description: INodeTypeDescription = {
displayName: 'Jenkins',
name: 'jenkins',
icon: 'file:jenkins.svg',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Jenkins API',
defaults: {
name: 'Jenkins',
color: '#04AA51',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'jenkinsApi',
required: true,
testedBy: 'jenkinApiCredentialTest',
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Build',
value: 'build',
},
{
name: 'Instance',
value: 'instance',
},
{
name: 'Job',
value: 'job',
},
],
default: 'job',
noDataExpression: true,
},
// --------------------------------------------------------------------------------------------------------
// Job Operations
// --------------------------------------------------------------------------------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'job',
],
},
},
options: [
{
name: 'Copy',
value: 'copy',
description: 'Copy a specific job',
},
{
name: 'Create',
value: 'create',
description: 'Create a new job',
},
{
name: 'Trigger',
value: 'trigger',
description: 'Trigger a specific job',
},
{
name: 'Trigger with Parameters',
value: 'triggerParams',
description: 'Trigger a specific job',
},
],
default: 'trigger',
description: 'Possible operations',
noDataExpression: true,
},
{
displayName: 'Make sure the job is setup to support triggering with parameters. <a href="https://wiki.jenkins.io/display/JENKINS/Parameterized+Build" target="_blank">More info</a>',
name: 'triggerParamsNotice',
type: 'notice',
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'triggerParams',
],
},
},
default: '',
},
{
displayName: 'Job Name',
name: 'job',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getJobs',
},
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'trigger',
'triggerParams',
'copy',
],
},
},
required: true,
default: '',
description: 'Name of the job',
},
// --------------------------------------------------------------------------------------------------------
// Trigger a Job
// --------------------------------------------------------------------------------------------------------
{
displayName: 'Parameters',
name: 'param',
type: 'fixedCollection',
placeholder: 'Add Parameter',
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'triggerParams',
],
},
},
required: true,
default: '',
typeOptions: {
multipleValues: true,
},
options: [
{
name: 'params',
displayName: 'Parameters',
values: [
{
displayName: 'Name',
name: 'name',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getJobParameters',
loadOptionsDependsOn: [
'job',
],
},
default: '',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
},
],
},
],
description: 'Parameters for Jenkins job',
},
// --------------------------------------------------------------------------------------------------------
// Copy or Create a Job
// --------------------------------------------------------------------------------------------------------
{
displayName: 'New Job Name',
name: 'newJob',
type: 'string',
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'copy',
'create',
],
},
},
required: true,
default: '',
description: 'Name of the new Jenkins job',
},
{
displayName: 'XML',
name: 'xml',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'create',
],
},
},
required: true,
default: '',
description: 'XML of Jenkins config',
},
{
displayName: 'To get the XML of an existing job, add config.xml to the end of the job URL',
name: 'createNotice',
type: 'notice',
default: '',
displayOptions: {
show: {
resource: [
'job',
],
operation: [
'create',
],
},
},
},
// --------------------------------------------------------------------------------------------------------
// Jenkins operations
// --------------------------------------------------------------------------------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'instance',
],
},
},
options: [
{
name: 'Cancel Quiet Down',
value: 'cancelQuietDown',
description: 'Cancel quiet down state',
},
{
name: 'Quiet Down',
value: 'quietDown',
description: 'Put Jenkins in quiet mode, no builds can be started, Jenkins is ready for shutdown',
},
{
name: 'Restart',
value: 'restart',
description: 'Restart Jenkins immediately on environments where it is possible',
},
{
name: 'Safely Restart',
value: 'safeRestart',
description: 'Restart Jenkins once no jobs are running on environments where it is possible',
},
{
name: 'Safely Shutdown',
value: 'safeExit',
description: 'Shutdown once no jobs are running',
},
{
name: 'Shutdown',
value: 'exit',
description: 'Shutdown Jenkins immediately',
},
],
default: 'safeRestart',
description: 'Jenkins instance operations',
noDataExpression: true,
},
{
displayName: 'Reason',
name: 'reason',
type: 'string',
displayOptions: {
show: {
resource: [
'instance',
],
operation: [
'quietDown',
],
},
},
required: false,
default: '',
description: 'Freeform reason for quiet down mode',
},
{
displayName: 'Instance operation can shutdown Jenkins instance and make it unresponsive. Some commands may not be available depending on instance implementation.',
name: 'instanceNotice',
type: 'notice',
default: '',
displayOptions: {
show: {
resource: [
'instance',
],
},
},
},
// --------------------------------------------------------------------------------------------------------
// Builds operations
// --------------------------------------------------------------------------------------------------------
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'build',
],
},
},
options: [
{
name: 'Get All',
value: 'getAll',
description: 'List Builds',
},
],
default: 'getAll',
noDataExpression: true,
},
{
displayName: 'Job Name',
name: 'job',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getJobs',
},
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'getAll',
],
},
},
required: true,
default: '',
description: 'Name of the job',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
default: false,
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'getAll',
],
},
},
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
default: 50,
typeOptions: {
minValue: 1,
},
displayOptions: {
show: {
resource: [
'build',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
description: 'Max number of results to return',
},
],
};
methods = {
credentialTest: {
async jenkinApiCredentialTest(
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<NodeCredentialTestResult> {
const { baseUrl, username, apiKey } = credential.data as JenkinsApiCredentials;
const url = tolerateTrailingSlash(baseUrl);
const endpoint = '/api/json';
const options = {
auth: {
username,
password: apiKey,
},
method: 'GET',
body: {},
qs: {},
uri: `${url}${endpoint}`,
json: true,
};
try {
await this.helpers.request(options);
return {
status: 'OK',
message: 'Authentication successful',
};
} catch (error) {
return {
status: 'Error',
message: error.message,
};
}
},
},
loadOptions: {
async getJobs(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const endpoint = `/api/json`;
const { jobs } = await jenkinsApiRequest.call(this, 'GET', endpoint);
for (const job of jobs) {
returnData.push({
name: job.name,
value: job.name,
});
}
returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return returnData;
},
async getJobParameters(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const job = this.getCurrentNodeParameter('job') as string;
const returnData: INodePropertyOptions[] = [];
const endpoint = `/job/${job}/api/json?tree=actions[parameterDefinitions[*]]`;
const { actions } = await jenkinsApiRequest.call(this, 'GET', endpoint);
for (const { _class, parameterDefinitions } of actions) {
if (_class?.includes('ParametersDefinitionProperty')) {
for (const { name, type } of parameterDefinitions) {
returnData.push({
name: `${name} - (${type})`,
value: name,
});
}
}
}
returnData.sort((a, b) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
});
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < length; i++) {
try {
if (resource === 'job') {
if (operation === 'trigger') {
const job = this.getNodeParameter('job', i) as string;
const endpoint = `/job/${job}/build`;
await jenkinsApiRequest.call(this, 'POST', endpoint);
responseData = { success: true };
}
if (operation === 'triggerParams') {
const job = this.getNodeParameter('job', i) as string;
const params = this.getNodeParameter('param.params', i, []) as [];
let body = {};
if (params.length) {
body = params.reduce((body: IDataObject, param: { name: string; value: string }) => {
body[param.name] = param.value;
return body;
}, {});
}
const endpoint = `/job/${job}/buildWithParameters`;
await jenkinsApiRequest.call(this, 'POST', endpoint, {}, {},
{
form: body,
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
},
);
responseData = { success: true };
}
if (operation === 'copy') {
const job = this.getNodeParameter('job', i) as string;
const name = this.getNodeParameter('newJob', i) as string;
const queryParams = {
name,
mode: 'copy',
from: job,
};
const endpoint = `/createItem`;
try {
await jenkinsApiRequest.call(this, 'POST', endpoint, queryParams);
responseData = { success: true };
}
catch (error) {
if (error.httpCode === '302') {
responseData = { success: true };
} else {
throw new NodeApiError(this.getNode(), error);
}
}
}
if (operation === 'create') {
const name = this.getNodeParameter('newJob', i) as string;
const queryParams = {
name,
};
const headers = {
'content-type': 'application/xml',
};
const body = this.getNodeParameter('xml', i) as string;
const endpoint = `/createItem`;
await jenkinsApiRequest.call(this, 'POST', endpoint, queryParams, body, { headers, json: false });
responseData = { success: true };
}
}
if (resource === 'instance') {
if (operation === 'quietDown') {
const reason = this.getNodeParameter('reason', i) as string;
let queryParams;
if (reason) {
queryParams = {
reason,
};
}
const endpoint = `/quietDown`;
await jenkinsApiRequest.call(this, 'POST', endpoint, queryParams);
responseData = { success: true };
}
if (operation === 'cancelQuietDown') {
const endpoint = `/cancelQuietDown`;
await jenkinsApiRequest.call(this, 'POST', endpoint);
responseData = { success: true };
}
if (operation === 'restart') {
const endpoint = `/restart`;
try {
await jenkinsApiRequest.call(this, 'POST', endpoint);
} catch (error) {
if (error.httpCode === '503') {
responseData = { success: true };
} else {
throw new NodeApiError(this.getNode(), error);
}
}
}
if (operation === 'safeRestart') {
const endpoint = `/safeRestart`;
try {
await jenkinsApiRequest.call(this, 'POST', endpoint);
} catch (error) {
if (error.httpCode === '503') {
responseData = { success: true };
} else {
throw new NodeApiError(this.getNode(), error);
}
}
}
if (operation === 'exit') {
const endpoint = `/exit`;
await jenkinsApiRequest.call(this, 'POST', endpoint);
responseData = { success: true };
}
if (operation === 'safeExit') {
const endpoint = `/safeExit`;
await jenkinsApiRequest.call(this, 'POST', endpoint);
responseData = { success: true };
}
}
if (resource === 'build') {
if (operation === 'getAll') {
const job = this.getNodeParameter('job', i) as string;
let endpoint = `/job/${job}/api/json?tree=builds[*]`;
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
if (!returnAll) {
const limit = this.getNodeParameter('limit', i) as number;
endpoint += `{0,${limit}}`;
}
responseData = await jenkinsApiRequest.call(this, 'GET', endpoint);
responseData = responseData.builds;
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({ error: error.message });
continue;
}
throw error;
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

View file

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

View file

@ -146,7 +146,7 @@ export function getConditions(options: IDataObject) {
const conditions = (options.conditionsUi as IDataObject || {}).conditionValues as IDataObject[]; const conditions = (options.conditionsUi as IDataObject || {}).conditionValues as IDataObject[];
let data = undefined; let data = undefined;
if (Array.isArray(conditions) && conditions.length !== 0) { if (Array.isArray(conditions) && conditions.length !== 0) {
data = conditions.map((condition: IDataObject) => `${condition.field}${(condition.operation) === 'equal' ? '=' : condition.operation}${getValue(condition.value)}`); data = conditions.map((condition: IDataObject) => `${condition.field} ${(condition.operation) === 'equal' ? '=' : condition.operation} ${getValue(condition.value)}`);
data = `WHERE ${data.join(' AND ')}`; data = `WHERE ${data.join(' AND ')}`;
} }
return data; return data;
@ -191,7 +191,9 @@ export function getQuery(options: IDataObject, sobject: string, returnAll: boole
} }
export function getValue(value: any) { // tslint:disable-line:no-any export function getValue(value: any) { // tslint:disable-line:no-any
if (typeof value === 'string') { if (moment(value).isValid()) {
return value;
} else if (typeof value === 'string') {
return `'${value}'`; return `'${value}'`;
} else { } else {
return value; return value;

View file

@ -0,0 +1,20 @@
{
"node": "n8n-nodes-base.supabase",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": [
"Data & Storage"
],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/credentials/supabase"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.supabase/"
}
]
}
}

View file

@ -31,11 +31,12 @@
"Filter", "Filter",
"Condition", "Condition",
"Logic", "Logic",
"Branch" "Branch",
"Case"
], ],
"subcategories": { "subcategories": {
"Core Nodes": [ "Core Nodes": [
"Flow" "Flow"
] ]
} }
} }

View file

@ -0,0 +1,20 @@
{
"node": "n8n-nodes-base.syncroMsp",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": [
"Productivity"
],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/credentials/syncroMsp"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.syncroMsp/"
}
]
}
}

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n-nodes-base", "name": "n8n-nodes-base",
"version": "0.156.0", "version": "0.157.0",
"description": "Base nodes of n8n", "description": "Base nodes of n8n",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",
@ -149,6 +149,7 @@
"dist/credentials/IntercomApi.credentials.js", "dist/credentials/IntercomApi.credentials.js",
"dist/credentials/InvoiceNinjaApi.credentials.js", "dist/credentials/InvoiceNinjaApi.credentials.js",
"dist/credentials/IterableApi.credentials.js", "dist/credentials/IterableApi.credentials.js",
"dist/credentials/JenkinsApi.credentials.js",
"dist/credentials/JiraSoftwareCloudApi.credentials.js", "dist/credentials/JiraSoftwareCloudApi.credentials.js",
"dist/credentials/JiraSoftwareServerApi.credentials.js", "dist/credentials/JiraSoftwareServerApi.credentials.js",
"dist/credentials/JotFormApi.credentials.js", "dist/credentials/JotFormApi.credentials.js",
@ -472,6 +473,7 @@
"dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js", "dist/nodes/InvoiceNinja/InvoiceNinjaTrigger.node.js",
"dist/nodes/ItemLists/ItemLists.node.js", "dist/nodes/ItemLists/ItemLists.node.js",
"dist/nodes/Iterable/Iterable.node.js", "dist/nodes/Iterable/Iterable.node.js",
"dist/nodes/Jenkins/Jenkins.node.js",
"dist/nodes/Jira/Jira.node.js", "dist/nodes/Jira/Jira.node.js",
"dist/nodes/Jira/JiraTrigger.node.js", "dist/nodes/Jira/JiraTrigger.node.js",
"dist/nodes/JotForm/JotFormTrigger.node.js", "dist/nodes/JotForm/JotFormTrigger.node.js",
@ -689,7 +691,7 @@
"@types/xml2js": "^0.4.3", "@types/xml2js": "^0.4.3",
"gulp": "^4.0.0", "gulp": "^4.0.0",
"jest": "^26.4.2", "jest": "^26.4.2",
"n8n-workflow": "~0.82.0", "n8n-workflow": "~0.83.0",
"nodelinter": "^0.1.9", "nodelinter": "^0.1.9",
"ts-jest": "^26.3.0", "ts-jest": "^26.3.0",
"tslint": "^6.1.2", "tslint": "^6.1.2",
@ -731,7 +733,7 @@
"mqtt": "4.2.6", "mqtt": "4.2.6",
"mssql": "^6.2.0", "mssql": "^6.2.0",
"mysql2": "~2.3.0", "mysql2": "~2.3.0",
"n8n-core": "~0.100.0", "n8n-core": "~0.101.0",
"node-ssh": "^12.0.0", "node-ssh": "^12.0.0",
"nodemailer": "^6.5.0", "nodemailer": "^6.5.0",
"pdf-parse": "^1.1.1", "pdf-parse": "^1.1.1",

View file

@ -10,9 +10,7 @@
"jest" "jest"
], ],
"module": "commonjs", "module": "commonjs",
"noImplicitAny": true,
"removeComments": true, "removeComments": true,
"strictNullChecks": true,
"strict": true, "strict": true,
"preserveConstEnums": true, "preserveConstEnums": true,
"resolveJsonModule": true, "resolveJsonModule": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "n8n-workflow", "name": "n8n-workflow",
"version": "0.82.0", "version": "0.83.0",
"description": "Workflow base code of n8n", "description": "Workflow base code of n8n",
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io", "homepage": "https://n8n.io",

View file

@ -9,9 +9,7 @@
"jest" "jest"
], ],
"module": "commonjs", "module": "commonjs",
"noImplicitAny": true,
"removeComments": true, "removeComments": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"strict": true, "strict": true,