UNPKG

n8n-nodes-awx

Version:

n8n node to interact with Ansible AWX/Tower with improved type safety

1,058 lines (1,055 loc) 54.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.formatResponse = exports.normalizeParameters = void 0; exports.getAwxResources = getAwxResources; exports.getAwxResource = getAwxResource; exports.createAwxResource = createAwxResource; exports.launchAwxTemplate = launchAwxTemplate; exports.updateAwxResource = updateAwxResource; exports.deleteAwxResourceWithLogging = deleteAwxResourceWithLogging; exports.cancelAwxJobGeneric = cancelAwxJobGeneric; exports.syncAwxResource = syncAwxResource; exports.deleteAwxResource = deleteAwxResource; exports.executeResourceHandler = executeResourceHandler; exports.processResourceResult = processResourceResult; exports.handleResourceOperation = handleResourceOperation; exports.getAwxResourceById = getAwxResourceById; exports.getAwxJobOutput = getAwxJobOutput; exports.executeAwxToolOperation = executeAwxToolOperation; exports.resolveResourceParameters = resolveResourceParameters; exports.createParameterResolutionErrorMessage = createParameterResolutionErrorMessage; exports.createParameterDiagnosticReport = createParameterDiagnosticReport; exports.executeProject = executeProject; exports.executeWorkflowJob = executeWorkflowJob; exports.executeJobTemplate = executeJobTemplate; exports.executeWorkflowJobTemplate = executeWorkflowJobTemplate; const WorkflowJobTemplateResources_1 = require("../../src/resources/WorkflowJobTemplateResources"); const n8n_workflow_1 = require("n8n-workflow"); const GenericFunctions_1 = require("./GenericFunctions"); async function getAwxResources(resourcePath, fetchMode = 'firstPage', query = {}, options = { timeout: null, waitForCompletion: null, pollInterval: null, maxRetries: null, forceRefresh: null, }) { if (fetchMode === 'allPages') { return await GenericFunctions_1.awxApiRequestAllItems.call(this, 'GET', resourcePath, {}, query, options); } else { const response = await GenericFunctions_1.awxApiRequest.call(this, 'GET', resourcePath, {}, query, options); console.log(`[AWX] getAwxResources response type: ${typeof response}, isArray: ${Array.isArray(response)}`, response ? Object.keys(response) : 'null'); if (response && typeof response === 'object' && 'results' in response) { console.log(`[AWX] Collection response found with ${response.results.length} results`); return response.results; } if (response && typeof response === 'object' && 'id' in response) { console.log('[AWX] Single-item response found with ID:', response.id); return [response]; } if (Array.isArray(response)) { return response; } if (response && typeof response === 'object' && Object.keys(response).length > 0) { console.log('[AWX] Unknown object response format, treating as single item'); return [response]; } console.log('[AWX] No valid response data, returning empty array'); return []; } } async function getAwxResource(resourceType, identifier, itemIndex = 0) { if (!identifier.id && !identifier.name) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Either ID or Name must be provided to get a ${resourceType}.`, { itemIndex }); } let resolvedResourceId; try { resolvedResourceId = await GenericFunctions_1.getResourceById.call(this, resourceType, identifier.id ? String(identifier.id) : undefined, identifier.name, itemIndex); console.log(`AWX Node: Resolved ${resourceType} identifier to ID ${resolvedResourceId}`); } catch (error) { console.error(`AWX Node: Error resolving ID for ${resourceType} with identifier ${JSON.stringify(identifier)}:`, error); if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } const errorMessage = error instanceof Error ? error.message : String(error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error resolving ID for ${resourceType}: ${errorMessage}`, { itemIndex }); } const resourceEndpoint = `/${resourceType}/${resolvedResourceId}/`; console.log(`AWX Node: Fetching ${resourceType} with ID ${resolvedResourceId}`); try { const resource = await GenericFunctions_1.awxApiRequest.call(this, 'GET', resourceEndpoint, {}); return resource; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`AWX Node: Error fetching ${resourceType} with ID ${resolvedResourceId}:`, error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error fetching ${resourceType} with ID ${resolvedResourceId}: ${errorMessage}`, { itemIndex }); } } async function createAwxResource(endpoint, bodyData, itemIndex = 0) { try { console.log(`AWX Node: POST request to ${endpoint} with data:`, JSON.stringify(bodyData)); const response = await GenericFunctions_1.awxApiRequest.call(this, 'POST', endpoint, bodyData); return response; } catch (error) { console.error(`AWX Node: Error in POST request to ${endpoint}:`, error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error creating AWX resource: ${error.message}`, { itemIndex }); } } async function launchAwxTemplate(resourceType, templateId, payload) { const endpoint = `/${resourceType}/${templateId}/launch/`; return createAwxResource.call(this, endpoint, payload); } async function updateAwxResource(endpoint, bodyData, itemIndex = 0) { try { console.log(`AWX Node: PATCH request to ${endpoint} with data:`, JSON.stringify(bodyData)); const response = await GenericFunctions_1.awxApiRequest.call(this, 'PATCH', endpoint, bodyData); return response; } catch (error) { console.error(`AWX Node: Error in PATCH request to ${endpoint}:`, error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error updating AWX resource: ${error.message}`, { itemIndex }); } } async function deleteAwxResourceWithLogging(endpoint, itemIndex = 0) { try { console.log(`AWX Node: DELETE request to ${endpoint}`); await GenericFunctions_1.awxApiRequest.call(this, 'DELETE', endpoint, {}); console.log(`AWX Node: Successfully deleted resource at ${endpoint}`); } catch (error) { console.error(`AWX Node: Error in DELETE request to ${endpoint}:`, error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error deleting AWX resource: ${error.message}`, { itemIndex }); } } async function cancelAwxJobGeneric(jobType, jobId, itemIndex = 0) { const cancelEndpoint = `/${jobType}/${jobId}/cancel/`; try { const requestOptions = { timeout: null, waitForCompletion: null, pollInterval: null, maxRetries: null, forceRefresh: null, itemIndex, }; return await GenericFunctions_1.awxApiRequest.call(this, 'POST', cancelEndpoint, {}, {}, requestOptions); } catch (error) { console.error(`[AWX] Failed to cancel ${jobType} job ${jobId}: ${error.message}`); throw error; } } async function syncAwxResource(endpoint, itemIndex = 0) { try { console.log(`AWX Node: POST request to ${endpoint} to trigger sync`); const response = await GenericFunctions_1.awxApiRequest.call(this, 'POST', endpoint, {}); return response; } catch (error) { console.error(`AWX Node: Error in POST request to ${endpoint}:`, error); throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error syncing AWX resource: ${error.message}`, { itemIndex }); } } async function deleteAwxResource(endpoint) { try { const credentials = await this.getCredentials('awxApi'); const options = { url: `${credentials.baseUrl}${endpoint}`, method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${credentials.apiKey}` }, json: true }; await this.helpers.httpRequest(options); return { success: true }; } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), error); } } const normalizeParameters = function (itemIndex = 0) { console.log('AWX Node: Starting parameter normalization...'); try { const inputData = this.getInputData(); if (!inputData || !inputData[itemIndex]) { console.log('AWX Node: No valid input data at index', itemIndex); return; } const inputJson = inputData[itemIndex].json; if (!inputJson || typeof inputJson !== 'object') { console.log('AWX Node: Input data is not a valid object', inputJson); return; } const allParameters = inputJson; console.log('AWX Node: Normalizing parameters:', JSON.stringify(allParameters)); const parameterMap = { 'Inventory_Name': 'inventoryName', 'Inventory_ID': 'inventoryId', 'Host_Name': 'hostName', 'Host_ID': 'hostId', 'Group_Name': 'groupName', 'Group_ID': 'groupId', 'Job_ID': 'jobId', 'Template_Name': 'templateName', 'Template_ID': 'templateId', 'Project_Name': 'projectName', 'Project_ID': 'projectId', 'Return_All': 'returnAll', 'Workflow_Template_Name': 'workflowTemplateName', 'Workflow_Job_ID': 'workflowJobId' }; console.log('AWX Node: Checking for special AI agent parameter mappings'); const aiParameterMappings = { 'inventory_name': 'inventoryName', 'inventoryname': 'inventoryName', 'inventory': 'inventoryName', 'group_name': 'groupName', 'groupname': 'groupName', 'group': 'groupName', 'group_id': 'groupId', 'id': 'groupId', 'host_name': 'hostName', 'hostname': 'hostName', 'host': 'hostName', 'job_id': 'jobId', 'jobid': 'jobId', 'template_name': 'templateName', 'templatename': 'templateName', 'template': 'templateName' }; for (const [aiKey, standardKey] of Object.entries(aiParameterMappings)) { if (allParameters[aiKey] !== undefined && allParameters[standardKey] === undefined) { console.log(`AWX Node: Mapping AI parameter ${aiKey} to ${standardKey}`); allParameters[standardKey] = allParameters[aiKey]; } } if (allParameters.resource && allParameters.id && typeof allParameters.id === 'string') { const resourceIdMap = { 'group': 'groupId', 'host': 'hostId', 'inventory': 'inventoryId', 'job': 'jobId', 'jobTemplate': 'templateId', 'project': 'projectId', 'workflowJobTemplate': 'workflowTemplateId', 'workflowJob': 'workflowJobId' }; const targetIdField = resourceIdMap[allParameters.resource]; if (targetIdField && !allParameters[targetIdField]) { console.log(`AWX Node: Mapping generic 'id' to resource-specific '${targetIdField}' based on resource type '${allParameters.resource}'`); allParameters[targetIdField] = allParameters.id; } } const needsResourceAction = !allParameters.resource || !allParameters.action; for (const [key, value] of Object.entries(allParameters)) { if (key === key.charAt(0).toLowerCase() + key.slice(1).replace(/_/g, '')) continue; if (parameterMap[key]) { const camelCaseKey = parameterMap[key]; console.log(`AWX Node: Converting parameter ${key} to ${camelCaseKey}`); allParameters[camelCaseKey] = value; delete allParameters[key]; } else { const camelCaseKey = key.charAt(0).toLowerCase() + key.slice(1).replace(/_/g, ''); console.log(`AWX Node: Converting unknown parameter ${key} to ${camelCaseKey}`); allParameters[camelCaseKey] = value; delete allParameters[key]; } } if (needsResourceAction) { if (!allParameters.resource) { if (allParameters.hostName || allParameters.hostId) { allParameters.resource = 'host'; console.log('AWX Node: Inferred resource type: host'); } else if (allParameters.inventoryName || allParameters.inventoryId) { allParameters.resource = 'inventory'; console.log('AWX Node: Inferred resource type: inventory'); } else if (allParameters.groupName || allParameters.groupId) { allParameters.resource = 'group'; console.log('AWX Node: Inferred resource type: group'); } else if (allParameters.templateName || allParameters.templateId) { allParameters.resource = 'jobTemplate'; console.log('AWX Node: Inferred resource type: jobTemplate'); } else if (allParameters.projectName || allParameters.projectId) { allParameters.resource = 'project'; console.log('AWX Node: Inferred resource type: project'); } else if (allParameters.workflowTemplateName) { allParameters.resource = 'workflowJobTemplate'; console.log('AWX Node: Inferred resource type: workflowJobTemplate'); } else if (allParameters.jobId) { allParameters.resource = 'job'; console.log('AWX Node: Inferred resource type: job'); } else if (allParameters.workflowJobId) { allParameters.resource = 'workflowJob'; console.log('AWX Node: Inferred resource type: workflowJob'); } else if (allParameters.returnAll) { allParameters.resource = 'host'; console.log('AWX Node: Inferred resource type: host (based on returnAll parameter)'); } } if (!allParameters.action && allParameters.resource) { if (allParameters.hostId || allParameters.inventoryId || allParameters.groupId || allParameters.templateId || allParameters.projectId || allParameters.jobId || allParameters.workflowJobId) { allParameters.action = 'get'; console.log('AWX Node: Inferred action: get (based on ID parameter)'); } else if (allParameters.returnAll === true) { allParameters.action = 'list'; console.log('AWX Node: Inferred action: list (based on returnAll parameter)'); } else if (allParameters.hostName || allParameters.inventoryName || allParameters.groupName || allParameters.templateName || allParameters.projectName || allParameters.workflowTemplateName) { allParameters.action = 'get'; console.log('AWX Node: Inferred action: get (based on name parameter)'); } else if (allParameters.resource === 'host' || allParameters.resource === 'group') { allParameters.action = 'list'; console.log('AWX Node: Inferred action: list (default for resource type)'); } } } console.log('AWX Node: Parameters after normalization:', JSON.stringify(allParameters)); } catch (error) { console.error('AWX Node: Error normalizing parameters:', error); } }; exports.normalizeParameters = normalizeParameters; const formatResponse = function (data) { if (Array.isArray(data)) { return [data.map(item => ({ json: item }))]; } else { return [[{ json: data }]]; } }; exports.formatResponse = formatResponse; async function executeResourceHandler(context, resourceType, action, handler, itemIndex) { try { return await handler.call(context, action, itemIndex !== undefined ? itemIndex : 0); } catch (error) { throw new Error(`Error in ${resourceType} operation: ${error.message}`); } } function processResourceResult(result) { if (result && result.length > 0 && result[0].length > 0) { return result[0][0].json; } else { return { success: true, message: 'Operation completed but no data returned' }; } } async function handleResourceOperation(context, resource, action, handler, specialParams = {}) { const paramOrder = specialParams.paramOrder || 'standard'; const needsItemIndex = specialParams.needsItemIndex !== undefined ? specialParams.needsItemIndex : false; const returnRaw = specialParams.returnRaw !== undefined ? specialParams.returnRaw : false; let wrapperFunction; if (paramOrder === 'itemIndexFirst') { wrapperFunction = async function (action, itemIndex) { return handler.call(this, itemIndex !== undefined ? itemIndex : 0, action); }; } else if (needsItemIndex) { wrapperFunction = async function (action, itemIndex) { return handler.call(this, action, itemIndex !== undefined ? itemIndex : 0); }; } else { wrapperFunction = async function (action) { return handler.call(this, action); }; } const result = await executeResourceHandler(context, resource, action, wrapperFunction); return returnRaw ? result : processResourceResult(result); } async function getAwxResourceById(resourceType, id, itemIndex = 0) { try { const formattedResourceType = resourceType.replace(/s$/, '') + 's'; const endpoint = `/${formattedResourceType}/${id}/`; console.log(`AWX Node: Fetching ${resourceType} with ID ${id} from endpoint ${endpoint}`); const requestOptions = { timeout: null, waitForCompletion: null, pollInterval: null, maxRetries: null, forceRefresh: null, itemIndex, }; const response = await GenericFunctions_1.awxApiRequest.call(this, 'GET', endpoint, {}, {}, requestOptions); if (!response) { throw new Error(`No response received when fetching ${resourceType} with ID ${id}`); } console.log(`AWX Node: Successfully retrieved ${resourceType} with ID ${id}`); return response; } catch (error) { console.error(`AWX Node: Error fetching ${resourceType} with ID ${id}:`, error); if (error.statusCode === 404 || (error.response && error.response.statusCode === 404)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${resourceType} with ID ${id} not found`, { itemIndex }); } throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error fetching ${resourceType} with ID ${id}: ${error.message}`, { itemIndex }); } } async function getAwxJobOutput(resourceType, id, itemIndex = 0, jobData) { try { const endpointFormats = [ `/${resourceType}/${id}/stdout/`, `/${resourceType}/${id}/stdout/?format=txt`, `/${resourceType}/${id}/stdout`, `/${resourceType}/${id}/?format=txt`, ]; let output = ''; let succeeded = false; for (const endpoint of endpointFormats) { try { console.log(`AWX Node: Trying to fetch ${resourceType} output with endpoint ${endpoint}`); const requestOptions = { timeout: null, waitForCompletion: null, pollInterval: null, maxRetries: null, forceRefresh: null, itemIndex, }; const response = await GenericFunctions_1.awxApiRequest.call(this, 'GET', endpoint, {}, {}, requestOptions); if (typeof response === 'string') { output = response; succeeded = true; console.log(`AWX Node: Retrieved ${resourceType} output as string (${output.length} characters)`); break; } else if (response && typeof response === 'object') { const responseObj = response; const outputFields = ['content', 'result', 'stdout', 'output']; for (const field of outputFields) { if (responseObj[field] && typeof responseObj[field] === 'string') { output = String(responseObj[field]); succeeded = true; console.log(`AWX Node: Retrieved ${resourceType} output from ${field} field (${output.length} characters)`); break; } } if (succeeded) break; } } catch (innerError) { console.log(`[AWX] Failed to fetch output with endpoint ${endpoint}: ${innerError.message}`); } } if (succeeded) { return output; } if (!jobData) { console.log(`AWX Node: Fetching ${resourceType} data to find output`); try { const data = await getAwxResourceById.call(this, resourceType, String(id), itemIndex); console.log(`AWX Node: Got ${resourceType} data, checking for output fields`); const outputFields = ['result_stdout', 'stdout', 'output', 'result', 'result_stdout_file', 'task_output']; for (const field of outputFields) { if (data[field] && typeof data[field] === 'string') { output = String(data[field]); console.log(`AWX Node: Retrieved ${resourceType} output from ${field} field (${output.length} characters)`); return output; } } } catch (dataError) { console.error(`[AWX] Failed to fetch ${resourceType} data for output: ${dataError.message}`); } } else { const outputFields = ['result_stdout', 'stdout', 'output', 'result', 'result_stdout_file', 'task_output']; for (const field of outputFields) { if (jobData[field] && typeof jobData[field] === 'string') { output = String(jobData[field]); console.log(`AWX Node: Retrieved ${resourceType} output from ${field} field (${output.length} characters)`); return output; } } } throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Failed to retrieve job output after multiple attempts'); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error getting AWX job output: ${error.message}`); } } async function executeAwxToolOperation(context) { var _a, _b; console.log('AWX Node: executeAwxToolOperation called'); try { const inputData = context.getInputData(); if (inputData && inputData[0] && inputData[0].json) { console.log('AWX Node: Raw input data before normalization:', JSON.stringify(inputData[0].json)); } else { console.log('AWX Node: No valid input data to normalize'); } } catch (error) { console.error('AWX Node: Error logging raw input data:', error); } exports.normalizeParameters.call(context, 0); try { const resource = context.getNodeParameter('resource', 0); const action = context.getNodeParameter('action', 0); console.log(`AWX Tool: Executing operation - Resource: ${resource}, Action: ${action}`); try { const inputData = ((_a = context.getInputData()[0]) === null || _a === void 0 ? void 0 : _a.json) || {}; console.log(`AWX Tool: Raw input data received: ${JSON.stringify(inputData, null, 2)}`); } catch (error) { console.error(`AWX Tool: Error logging input data: ${error.message}`); } if (resource === 'diagnostic' && action === 'parameters') { const diagnosticReport = { timestamp: new Date().toISOString(), message: 'AWX Tool Parameter Diagnostic', parameterMappings: { jobTemplate: { idKeys: ['Job_Template_ID', 'Template_ID', 'jobTemplateId', 'jobTemplateID', 'templateId', 'id'], nameKeys: ['Template_Name', 'templateName', 'name'] }, inventory: { idKeys: ['Inventory_ID', 'inventoryId', 'inventoryID', 'id'], nameKeys: ['Inventory_Name', 'inventoryName', 'name'] }, host: { idKeys: ['Host_ID', 'hostId', 'hostID', 'id'], nameKeys: ['Host_Name', 'hostName', 'name'] }, group: { idKeys: ['Group_ID', 'groupId', 'groupID', 'id'], nameKeys: ['Group_Name', 'groupName', 'name'] } }, rawInput: {}, normalizedParameters: {} }; const inputData = ((_b = context.getInputData()[0]) === null || _b === void 0 ? void 0 : _b.json) || {}; if (inputData) { const normalizedParams = {}; for (const [key, value] of Object.entries(inputData)) { let newKey = key; if (key.includes('_')) { newKey = key.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()); } if (key === 'Job_Template_ID') newKey = 'jobTemplateID'; if (key === 'Template_Name') newKey = 'templateName'; if (key === 'Inventory_ID') newKey = 'inventoryID'; if (key === 'Inventory_Name') newKey = 'inventoryName'; normalizedParams[newKey] = value; console.log(`AWX Node: Parameter transformation: ${key} → ${newKey}`); } diagnosticReport.normalizedParameters = normalizedParams; } return [[{ json: diagnosticReport }]]; } const { handleProjectOperations } = await Promise.resolve().then(() => __importStar(require('../resources/ProjectResources'))); const { handleInventoryOperations } = await Promise.resolve().then(() => __importStar(require('../resources/InventoryResources'))); const { handleGroupOperations } = await Promise.resolve().then(() => __importStar(require('../resources/GroupResources'))); const { handleHostOperations } = await Promise.resolve().then(() => __importStar(require('../resources/HostResources'))); const { handleJobOperations } = await Promise.resolve().then(() => __importStar(require('../resources/JobResources'))); const { handleJobTemplateOperations } = await Promise.resolve().then(() => __importStar(require('../resources/JobTemplateResources'))); const { handleWorkflowJobTemplateOperations, handleWorkflowJobOperations } = await Promise.resolve().then(() => __importStar(require('../resources/WorkflowJobTemplateResources'))); switch (resource) { case 'project': return await handleResourceOperation(context, 'project', action, handleProjectOperations, { returnRaw: true }); case 'inventory': return await handleResourceOperation(context, 'inventory', action, handleInventoryOperations, { returnRaw: true }); case 'group': return await handleResourceOperation(context, 'group', action, handleGroupOperations, { returnRaw: true }); case 'host': return await handleResourceOperation(context, 'host', action, handleHostOperations, { returnRaw: true }); case 'jobTemplate': return await handleResourceOperation(context, 'jobTemplate', action, handleJobTemplateOperations, { returnRaw: true }); case 'job': return await handleResourceOperation(context, 'job', action, handleJobOperations, { returnRaw: true }); case 'workflowJobTemplate': return await handleResourceOperation(context, 'workflowJobTemplate', action, handleWorkflowJobTemplateOperations, { returnRaw: true }); case 'workflowJob': return await handleResourceOperation(context, 'workflowJob', action, handleWorkflowJobOperations, { returnRaw: true }); default: throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Unsupported resource type: ${resource}`); } } catch (error) { console.error(`AWX Node: Error in executeAwxToolOperation: ${error.message}`); throw error; } } function resolveResourceParameters(resourceType, idKeys, nameKeys, logPrefix = '') { var _a; console.log(`=== ${logPrefix || resourceType.toUpperCase()} - PARAMETER RESOLUTION ===`); console.log(`Looking for ID keys: ${JSON.stringify(idKeys)}`); console.log(`Looking for Name keys: ${JSON.stringify(nameKeys)}`); let id = ''; let name = ''; console.log('Checking node parameters (UI-based):'); for (const key of idKeys) { try { id = this.getNodeParameter(key, 0, ''); if (id) { console.log(`✓ Found ${key} in node parameters: ${id}`); break; } else { console.log(`✗ ${key} exists in node parameters but is empty`); } } catch (error) { console.log(`✗ Parameter ${key} not found in node parameters`); } } if (!id) { for (const key of nameKeys) { try { name = this.getNodeParameter(key, 0, ''); if (name) { console.log(`✓ Found ${key} in node parameters: ${name}`); break; } else { console.log(`✗ ${key} exists in node parameters but is empty`); } } catch (error) { console.log(`✗ Parameter ${key} not found in node parameters`); } } } if (!id && !name) { console.log('Checking execution data parameters (for AI agent tools):'); const inputData = this.getInputData()[0]; console.log(`Input data available: ${inputData ? 'Yes' : 'No'}`); if (inputData) { console.log(`Input data type: ${typeof inputData.json}`); console.log('Full input data:', JSON.stringify(inputData.json, null, 2)); const directParameters = inputData.json || {}; const nestedParameters = ((_a = inputData.json) === null || _a === void 0 ? void 0 : _a.parameters) || {}; console.log('Direct parameters available:', Object.keys(directParameters).length > 0 ? 'Yes' : 'No'); console.log('Nested parameters available:', Object.keys(nestedParameters).length > 0 ? 'Yes' : 'No'); const allPossibleParams = [directParameters, nestedParameters]; for (const params of allPossibleParams) { console.log('Checking parameter set:', JSON.stringify(params, null, 2)); for (const key of idKeys) { if (params[key] !== undefined) { id = params[key]; console.log(`✓ Found ${key} in parameters: ${id}`); break; } else { console.log(`✗ ${key} not found in this parameter set`); } } if (id) break; if (!id) { for (const key of nameKeys) { if (params[key] !== undefined) { name = params[key]; console.log(`✓ Found ${key} in parameters: ${name}`); break; } else { console.log(`✗ ${key} not found in this parameter set`); } } if (name) break; } } } } console.log(`Final parameter values - ID: ${id || '(not provided)'}, Name: ${name || '(not provided)'}`); return { id, name }; } function createParameterResolutionErrorMessage(resourceType, idKeys, nameKeys) { var _a; const inputData = ((_a = this.getInputData()[0]) === null || _a === void 0 ? void 0 : _a.json) || {}; return `Either ${resourceType} ID or Name must be provided. Expected parameters: - ID keys: ${JSON.stringify(idKeys)} - Name keys: ${JSON.stringify(nameKeys)} Received parameters: ${JSON.stringify(inputData, null, 2)} After normalization, no matching ID or name parameter was found.`; } function createParameterDiagnosticReport(resourceType, idKeys, nameKeys) { var _a; const inputData = ((_a = this.getInputData()[0]) === null || _a === void 0 ? void 0 : _a.json) || {}; const parameters = inputData.parameters || {}; const availableDirectParams = Object.keys(inputData); const availableNestedParams = Object.keys(parameters); const foundIdKeys = idKeys.filter(key => inputData[key] !== undefined || parameters[key] !== undefined); const foundNameKeys = nameKeys.filter(key => inputData[key] !== undefined || parameters[key] !== undefined); return { timestamp: new Date().toISOString(), resourceType, expectedParameters: { idKeys, nameKeys }, receivedParameters: { directParameters: inputData, nestedParameters: parameters }, parameterAnalysis: { availableDirectParams, availableNestedParams, foundIdKeys, foundNameKeys, missingIdKeys: idKeys.filter(key => !foundIdKeys.includes(key)), missingNameKeys: nameKeys.filter(key => !foundNameKeys.includes(key)) } }; } async function executeProject(inputs) { if (inputs.resource !== 'project') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid resource type '${inputs.resource}' for project operation`); } try { switch (inputs.action) { case 'get': { const response = await getAwxResources.call(this, `/projects/${inputs.id}/`); const projectData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: projectData, pairedItem: { item: 0 } }]]; } case 'list': { const projects = await getAwxResources.call(this, '/projects/'); return [projects.map((project, i) => { const itemData = typeof project === 'string' ? JSON.parse(project) : project; return { json: itemData, pairedItem: { item: i } }; })]; } case 'update': { const response = await getAwxResources.call(this, `/projects/${inputs.id}/`); const projectData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: projectData, pairedItem: { item: 0 } }]]; } case 'delete': { await deleteAwxResource.call(this, `/projects/${inputs.id}/`); return [[{ json: { success: true }, pairedItem: { item: 0 } }]]; } default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported project action: ${inputs.action}`); } } catch (error) { if (error instanceof Error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Project operation failed: ${error.message}`); } throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Unknown error during project operation'); } } async function executeWorkflowJob(inputs) { try { let responseData; const returnData = []; switch (inputs.action) { case 'get': { if (!inputs.jobId) throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Workflow Job ID required'); responseData = await GenericFunctions_1.awxApiRequest.call(this, 'GET', `/workflow_jobs/${inputs.jobId}/`); returnData.push(responseData); break; } case 'list': { const response = await GenericFunctions_1.awxApiRequest.call(this, 'GET', '/workflow_jobs/'); returnData.push(...(Array.isArray(response.results) ? response.results : [response])); break; } case 'delete': { if (!inputs.jobId) throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Workflow Job ID required'); await deleteAwxResource.call(this, `/workflow_jobs/${inputs.jobId}/`); returnData.push({ success: true }); break; } default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Action '${inputs.action}' not implemented for workflow jobs`); } return [this.helpers.returnJsonArray(returnData)]; } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), error); } } async function executeJobTemplate(inputs) { if (inputs.resource !== 'jobTemplate' && inputs.resource !== 'job' && inputs.resource !== 'workflowJob') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid resource type '${inputs.resource}' for job template operation`); } try { switch (inputs.action) { case 'get': { const endpoint = inputs.resource === 'workflowJob' ? `/workflow_jobs/${inputs.id}/` : `/job_templates/${inputs.id}/`; const response = await getAwxResources.call(this, endpoint); const jobTemplateData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: jobTemplateData, pairedItem: { item: 0 } }]]; } case 'list': { const endpoint = inputs.resource === 'workflowJob' ? '/workflow_jobs/' : '/job_templates/'; const templates = await getAwxResources.call(this, endpoint); return [templates.map((t, i) => { const itemData = typeof t === 'string' ? JSON.parse(t) : t; return { json: itemData, pairedItem: { item: i } }; })]; } case 'launch': { const endpoint = `/job_templates/${inputs.id}/launch/`; const response = await getAwxResources.call(this, endpoint); const jobTemplateData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: jobTemplateData, pairedItem: { item: 0 } }]]; } case 'setTemplate': { const response = await getAwxResources.call(this, `/job_templates/${inputs.id}/`); const jobTemplateData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: jobTemplateData, pairedItem: { item: 0 } }]]; } case 'create': { const response = await getAwxResources.call(this, '/job_templates/'); const jobTemplateData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: jobTemplateData, pairedItem: { item: 0 } }]]; } case 'update': { const response = await getAwxResources.call(this, `/job_templates/${inputs.id}/`); const jobTemplateData = Array.isArray(response) && response.length > 0 ? (typeof response[0] === 'string' ? JSON.parse(response[0]) : response[0]) : response; return [[{ json: jobTemplateData, pairedItem: { item: 0 } }]]; } case 'delete': { await deleteAwxResource.call(this, `/job_templates/${inputs.id}/`); return [[{ json: { success: true }, pairedItem: { item: 0 } }]]; } default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported job template action: ${inputs.action}`); } } catch (error) { if (error instanceof Error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Job template operation failed: ${error.message}`); } throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Unknown error during job template operation'); } } async function executeWorkflowJobTemplate(inputs) { if (inputs.resource !== 'workflowJobTemplate') { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid resource type '${inputs.resource}' for workflow job operation`); } const templateId = parseInt(inputs.id, 10); const itemIndex = 0; const hostname = this.getNodeParameter('hostname', itemIndex, ''); const credentials = await this.getCredentials('awxApi'); const { ApiClient } = require('../../src/ApiClient'); const apiClient = new ApiClient({ baseURL: hostname, auth: { username: credentials.username, password: credentials.password, }, }); const resources = new WorkflowJobTemplateResources_1.WorkflowJobTemplateResources(apiClient); const toIDataObject = (value) => { if (value === null || value === undefined) { return {}; } if (typeof value === 'object') { if (value && 'json' in value) { return value; } const result = {}; for (const [key, val] of Object.entries(value)) { result[key] = val; } return result; } return { value }; }; const ensureIDataObject = (value) => { return toIDataObject(value); }; const getBoundMethod = (methodName, errorMessage) => { const resourceRecord = resources; const method = resourceRecord[methodName]; if (typeof method !== 'function') { throw new Error(errorMessage); } return ((...args) => method(...args)).bind(resources); }; const getNotificationMethod = (type) => { const methodName = `getNotificationTemplates${type}`; return getBoundMethod(methodName, `Method ${methodName} not found on WorkflowJobTemplateResources`); }; const associateNotificationMethod = (type) => { const methodName = `associateNotificationTemplate${type}`; return getBoundMethod(methodName, `Method ${methodName} not found on WorkflowJobTemplateResources`); }; const disassociateNotificationMethod = (type) => { const methodName = `disassociateNotificationTemplate${type}`; return getBoundMethod(methodName, `Method ${methodName} not found on WorkflowJobTemplateResources`); }; try { switch (inputs.action) { case 'get': { const workflowJobTemplate = await resources.get(templateId); return [[{ json: ensureIDataObject(workflowJobTemplate), pairedItem: { item: 0 } }]]; } case 'list': { const workflowJobTemplates = await resources.list(); return [workflowJobTemplates.map((template, i) => ({ json: toIDataObject(template), pairedItem: { item: i } }))]; } case 'create': { const createData = { name: inputs.name, description: inputs.description, organization: inputs.organizationId, type: 'workflow_job_template', }; const response = await resources.create(createData); return [[{ json: toIDataObject(response), pairedItem: { item: 0 } }]]; } case 'update': { const updateData = {}; if (inputs.name !== undefined) updateData.name = inputs.name; if (inputs.description !== undefined) updateData.description = inputs.description; const response = await resources.update(templateId, updateData); return [[{ json: toIDataObject(response), pairedItem: { item: 0 } }]]; } case 'delete': { await resources.delete(templateId); return [[{ json: { success: true }, pairedItem: { item: 0 } }]]; } case 'launch': { const launchParams = { extra_vars: inputs.extraVars || {}, }; const response = await resources.launch(templateId, launchParams); return [[{ json: response, pairedItem: { item: 0 } }]]; } case