2023-01-27 03:22:44 -08:00
import type { IExecuteFunctions } from 'n8n-core' ;
2022-01-15 04:19:37 -08:00
2023-01-27 03:22:44 -08:00
import type {
2022-01-15 04:19:37 -08:00
ICredentialsDecrypted ,
ICredentialTestFunctions ,
IDataObject ,
ILoadOptionsFunctions ,
2022-02-05 13:55:43 -08:00
INodeCredentialTestResult ,
2022-01-15 04:19:37 -08:00
INodeExecutionData ,
INodePropertyOptions ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2023-01-27 03:22:44 -08:00
import { NodeApiError } from 'n8n-workflow' ;
2022-01-15 04:19:37 -08:00
2022-08-17 08:50:24 -07:00
import { jenkinsApiRequest , tolerateTrailingSlash } from './GenericFunctions' ;
2022-01-15 04:19:37 -08:00
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' ,
} ,
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 : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
options : [
{
name : 'Copy' ,
value : 'copy' ,
description : 'Copy a specific job' ,
2022-07-10 13:50:51 -07:00
action : 'Copy a job' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Create' ,
value : 'create' ,
description : 'Create a new job' ,
2022-07-10 13:50:51 -07:00
action : 'Create a job' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Trigger' ,
value : 'trigger' ,
description : 'Trigger a specific job' ,
2022-07-10 13:50:51 -07:00
action : 'Trigger a job' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Trigger with Parameters' ,
value : 'triggerParams' ,
description : 'Trigger a specific job' ,
2022-07-10 13:50:51 -07:00
action : 'Trigger a job with parameters' ,
2022-01-15 04:19:37 -08:00
} ,
] ,
default : 'trigger' ,
description : 'Possible operations' ,
noDataExpression : true ,
} ,
{
2022-08-17 08:50:24 -07:00
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>' ,
2022-01-15 04:19:37 -08:00
name : 'triggerParamsNotice' ,
type : 'notice' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'triggerParams' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
default : '' ,
} ,
{
2022-06-03 10:23:49 -07:00
displayName : 'Job Name or ID' ,
2022-01-15 04:19:37 -08:00
name : 'job' ,
type : 'options' ,
typeOptions : {
loadOptionsMethod : 'getJobs' ,
} ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'trigger' , 'triggerParams' , 'copy' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
required : true ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Name of the job. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
2022-01-15 04:19:37 -08:00
} ,
// --------------------------------------------------------------------------------------------------------
// Trigger a Job
// --------------------------------------------------------------------------------------------------------
{
displayName : 'Parameters' ,
name : 'param' ,
type : 'fixedCollection' ,
placeholder : 'Add Parameter' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'triggerParams' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
required : true ,
2022-04-22 09:29:51 -07:00
default : { } ,
2022-01-15 04:19:37 -08:00
typeOptions : {
multipleValues : true ,
} ,
options : [
{
name : 'params' ,
displayName : 'Parameters' ,
values : [
{
2022-06-03 10:23:49 -07:00
displayName : 'Name or ID' ,
2022-01-15 04:19:37 -08:00
name : 'name' ,
type : 'options' ,
2022-08-17 08:50:24 -07:00
description :
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>' ,
2022-01-15 04:19:37 -08:00
typeOptions : {
loadOptionsMethod : 'getJobParameters' ,
2022-08-17 08:50:24 -07:00
loadOptionsDependsOn : [ 'job' ] ,
2022-01-15 04:19:37 -08:00
} ,
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 : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'copy' , 'create' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
required : true ,
default : '' ,
description : 'Name of the new Jenkins job' ,
} ,
{
displayName : 'XML' ,
name : 'xml' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'create' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
required : true ,
default : '' ,
description : 'XML of Jenkins config' ,
} ,
{
2022-08-17 08:50:24 -07:00
displayName :
'To get the XML of an existing job, add ‘ config.xml’ to the end of the job URL' ,
2022-01-15 04:19:37 -08:00
name : 'createNotice' ,
type : 'notice' ,
default : '' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'job' ] ,
operation : [ 'create' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
} ,
// --------------------------------------------------------------------------------------------------------
// Jenkins operations
// --------------------------------------------------------------------------------------------------------
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'instance' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
options : [
{
name : 'Cancel Quiet Down' ,
value : 'cancelQuietDown' ,
description : 'Cancel quiet down state' ,
2022-07-10 13:50:51 -07:00
action : 'Cancel Quiet Down an instance' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Quiet Down' ,
value : 'quietDown' ,
2022-08-17 08:50:24 -07:00
description :
'Put Jenkins in quiet mode, no builds can be started, Jenkins is ready for shutdown' ,
2022-07-10 13:50:51 -07:00
action : 'Quiet Down an instance' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Restart' ,
value : 'restart' ,
description : 'Restart Jenkins immediately on environments where it is possible' ,
2022-07-10 13:50:51 -07:00
action : 'Restart an instance' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Safely Restart' ,
value : 'safeRestart' ,
2022-08-17 08:50:24 -07:00
description :
'Restart Jenkins once no jobs are running on environments where it is possible' ,
2022-07-10 13:50:51 -07:00
action : 'Safely Restart an instance' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Safely Shutdown' ,
value : 'safeExit' ,
description : 'Shutdown once no jobs are running' ,
2022-07-10 13:50:51 -07:00
action : 'Safely Shutdown an instance' ,
2022-01-15 04:19:37 -08:00
} ,
{
name : 'Shutdown' ,
value : 'exit' ,
description : 'Shutdown Jenkins immediately' ,
2022-07-10 13:50:51 -07:00
action : 'Shutdown an instance' ,
2022-01-15 04:19:37 -08:00
} ,
] ,
default : 'safeRestart' ,
description : 'Jenkins instance operations' ,
noDataExpression : true ,
} ,
{
displayName : 'Reason' ,
name : 'reason' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'instance' ] ,
operation : [ 'quietDown' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
default : '' ,
description : 'Freeform reason for quiet down mode' ,
} ,
{
2022-08-17 08:50:24 -07:00
displayName :
'Instance operation can shutdown Jenkins instance and make it unresponsive. Some commands may not be available depending on instance implementation.' ,
2022-01-15 04:19:37 -08:00
name : 'instanceNotice' ,
type : 'notice' ,
default : '' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'instance' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
} ,
// --------------------------------------------------------------------------------------------------------
// Builds operations
// --------------------------------------------------------------------------------------------------------
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'build' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
options : [
{
2022-09-07 07:51:14 -07:00
name : 'Get Many' ,
2022-01-15 04:19:37 -08:00
value : 'getAll' ,
description : 'List Builds' ,
2022-09-08 08:10:13 -07:00
action : 'Get many builds' ,
2022-01-15 04:19:37 -08:00
} ,
] ,
default : 'getAll' ,
noDataExpression : true ,
} ,
{
2022-06-03 10:23:49 -07:00
displayName : 'Job Name or ID' ,
2022-01-15 04:19:37 -08:00
name : 'job' ,
type : 'options' ,
typeOptions : {
loadOptionsMethod : 'getJobs' ,
} ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'build' ] ,
operation : [ 'getAll' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
required : true ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Name of the job. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
2022-01-15 04:19:37 -08:00
} ,
{
displayName : 'Return All' ,
name : 'returnAll' ,
type : 'boolean' ,
default : false ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
resource : [ 'build' ] ,
operation : [ 'getAll' ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
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 : {
2022-08-17 08:50:24 -07:00
resource : [ 'build' ] ,
operation : [ 'getAll' ] ,
returnAll : [ false ] ,
2022-01-15 04:19:37 -08:00
} ,
} ,
description : 'Max number of results to return' ,
} ,
] ,
} ;
methods = {
credentialTest : {
async jenkinApiCredentialTest (
this : ICredentialTestFunctions ,
credential : ICredentialsDecrypted ,
2022-02-05 13:55:43 -08:00
) : Promise < INodeCredentialTestResult > {
2022-01-15 04:19:37 -08:00
const { baseUrl , username , apiKey } = credential . data as JenkinsApiCredentials ;
const url = tolerateTrailingSlash ( baseUrl ) ;
const endpoint = '/api/json' ;
const options = {
auth : {
2022-01-15 11:33:30 -08:00
username ,
2022-01-15 04:19:37 -08:00
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 [ ] = [ ] ;
2022-12-29 03:20:43 -08:00
const endpoint = '/api/json' ;
2022-01-15 04:19:37 -08:00
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 ) = > {
2022-08-17 08:50:24 -07:00
if ( a . name < b . name ) {
return - 1 ;
}
if ( a . name > b . name ) {
return 1 ;
}
2022-01-15 04:19:37 -08:00
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 ) = > {
2022-08-17 08:50:24 -07:00
if ( a . name < b . name ) {
return - 1 ;
}
if ( a . name > b . name ) {
return 1 ;
}
2022-01-15 04:19:37 -08:00
return 0 ;
} ) ;
return returnData ;
} ,
} ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
const returnData : IDataObject [ ] = [ ] ;
2022-04-22 09:29:51 -07:00
const length = items . length ;
2022-01-15 04:19:37 -08:00
let responseData ;
2022-12-02 03:53:59 -08:00
const resource = this . getNodeParameter ( 'resource' , 0 ) ;
const operation = this . getNodeParameter ( 'operation' , 0 ) ;
2022-01-15 04:19:37 -08:00
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 [ ] ;
2022-12-02 12:54:28 -08:00
let form = { } ;
2022-01-15 04:19:37 -08:00
if ( params . length ) {
2022-12-02 12:54:28 -08:00
form = params . reduce ( ( body : IDataObject , param : { name : string ; value : string } ) = > {
2022-01-15 04:19:37 -08:00
body [ param . name ] = param . value ;
return body ;
} , { } ) ;
}
const endpoint = ` /job/ ${ job } /buildWithParameters ` ;
2022-08-17 08:50:24 -07:00
await jenkinsApiRequest . call (
this ,
'POST' ,
endpoint ,
{ } ,
{ } ,
2022-01-15 04:19:37 -08:00
{
2022-12-02 12:54:28 -08:00
form ,
2022-01-15 04:19:37 -08:00
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 ,
} ;
2022-12-29 03:20:43 -08:00
const endpoint = '/createItem' ;
2022-01-15 04:19:37 -08:00
try {
await jenkinsApiRequest . call ( this , 'POST' , endpoint , queryParams ) ;
responseData = { success : true } ;
2022-08-17 08:50:24 -07:00
} catch ( error ) {
2022-01-15 04:19:37 -08:00
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 ;
2022-12-29 03:20:43 -08:00
const endpoint = '/createItem' ;
2022-08-17 08:50:24 -07:00
await jenkinsApiRequest . call ( this , 'POST' , endpoint , queryParams , body , {
headers ,
json : false ,
} ) ;
2022-01-15 04:19:37 -08:00
responseData = { success : true } ;
}
}
if ( resource === 'instance' ) {
if ( operation === 'quietDown' ) {
const reason = this . getNodeParameter ( 'reason' , i ) as string ;
let queryParams ;
if ( reason ) {
queryParams = {
reason ,
} ;
}
2022-12-29 03:20:43 -08:00
const endpoint = '/quietDown' ;
2022-01-15 04:19:37 -08:00
await jenkinsApiRequest . call ( this , 'POST' , endpoint , queryParams ) ;
responseData = { success : true } ;
}
if ( operation === 'cancelQuietDown' ) {
2022-12-29 03:20:43 -08:00
const endpoint = '/cancelQuietDown' ;
2022-01-15 04:19:37 -08:00
await jenkinsApiRequest . call ( this , 'POST' , endpoint ) ;
responseData = { success : true } ;
}
if ( operation === 'restart' ) {
2022-12-29 03:20:43 -08:00
const endpoint = '/restart' ;
2022-01-15 04:19:37 -08:00
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' ) {
2022-12-29 03:20:43 -08:00
const endpoint = '/safeRestart' ;
2022-01-15 04:19:37 -08:00
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' ) {
2022-12-29 03:20:43 -08:00
const endpoint = '/exit' ;
2022-01-15 04:19:37 -08:00
await jenkinsApiRequest . call ( this , 'POST' , endpoint ) ;
responseData = { success : true } ;
}
if ( operation === 'safeExit' ) {
2022-12-29 03:20:43 -08:00
const endpoint = '/safeExit' ;
2022-01-15 04:19:37 -08:00
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[*] ` ;
2022-11-18 05:31:38 -08:00
const returnAll = this . getNodeParameter ( 'returnAll' , i ) ;
2022-01-15 04:19:37 -08:00
if ( ! returnAll ) {
2022-11-18 06:26:22 -08:00
const limit = this . getNodeParameter ( 'limit' , i ) ;
2022-01-15 04:19:37 -08:00
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 ) ] ;
}
}