UNPKG

n8n-nodes-arubacentral

Version:

n8n community node for Aruba Central API integration with comprehensive monitoring, configuration, and management capabilities

470 lines (398 loc) 13.9 kB
# Prompt for Implementing Aruba Central API Node Integration from OpenAPI Specification You are tasked with implementing a custom n8n integration node for the Aruba Central API. Below are comprehensive instructions for converting an OpenAPI specification into fully functional code following our established architecture pattern. When given an OpenAPI JSON file, you'll need to analyze it and generate complete implementation code. ## Your Task Given an OpenAPI specification for an Aruba Central API endpoint, you must generate all necessary files and code to integrate it into our existing Aruba Central n8n node following our Domain-Resource-Operation hierarchy pattern. ## Architecture Overview Our node uses a domain-driven, resource-oriented architecture: - **Domain** - High-level API area (monitoring, firmware, configuration) - **Resource** - Entity being manipulated (ap, client, gateway) - **Operation** - Specific action on a resource (getAps, updateClient) ## File Structure Convention Follow this strict file naming convention: ``` api/ └── {domain}/ ├── {domain}.operations.ts # Domain-level operation registration ├── {domain}.descriptions.ts # Domain-level UI elements └── {resource}/ ├── {resource}.common.ts # Shared utilities ├── {resource}.descriptions.ts # Resource-level UI description ├── {resource}.methods.ts # Implementation exports ├── {resource}.operations.ts # Operation registration ├── {resource}.types.ts # TypeScript interfaces └── operations/ ├── {operation}.descriptions.ts # Operation parameters └── {operation}.methods.ts # Implementation code ``` ## Implementation Process ### 1. Analyze OpenAPI Specification 1. Identify the appropriate domain based on the API paths 2. Identify resources from path segments 3. Map endpoint HTTP methods to operations 4. Extract parameters, request bodies, and responses ### 2. Create Resource Types Define TypeScript interfaces in `{resource}.types.ts`: ```typescript // Example for {resource}.types.ts export interface ResourceResponse { id: string; name: string; // Other properties from schema definition } export interface ResourceListParams { limit?: number; offset?: number; // Other query parameters } export function validateResourceResponse(response: any): response is ResourceResponse { return typeof response === 'object' && response !== null && typeof response.id === 'string'; } ``` ### 3. Implement Operation Methods For each operation, create `{operation}.methods.ts`: ```typescript // Example for {operation}.methods.ts import { IExecuteFunctions, INodeExecutionData, IDataObject } from 'n8n-workflow'; import { apiRequest } from '../../../../helpers/apiRequest'; import { logger } from '../../../../helpers/logger'; import { handleApiError } from '../../../../helpers/errorHandler'; import { handlePagination } from '../../../../helpers/pagination'; /** * {Description from OpenAPI} * {HTTP Method} {API Path} * * @param this The n8n execution context * @returns Formatted API response */ export async function operationName(this: IExecuteFunctions): Promise<INodeExecutionData[]> { try { // Get parameters from the node const returnAll = this.getNodeParameter('returnAll', 0, false) as boolean; const limit = returnAll ? 0 : this.getNodeParameter('limit', 0, 50) as number; // Get additional fields const additionalFields = this.getNodeParameter('additionalFields', 0, {}) as IDataObject; // Construct request options const options: IDataObject = {}; const qs: IDataObject = {}; // Add all query parameters from additionalFields const paramMap = { paramName: 'api_param_name', // Map UI field names to API parameter names }; // Process parameters for (const [field, apiParam] of Object.entries(paramMap)) { if (additionalFields[field] !== undefined) { qs[apiParam] = additionalFields[field]; } } options.qs = qs; // Handle pagination for list endpoints if (returnAll || limit > 0) { return await handlePagination.call( this, '/api/endpoint/path', 'GET', {}, // body qs, { path: ['data', 'items'], fallbackPaths: [['items'], ['data']], }, returnAll, limit, 100 // batch size ); } // For non-paginated endpoints const responseData = await apiRequest.call( this, 'GET', '/api/endpoint/path', {}, // body qs ); return [{ json: responseData }]; } catch (error) { logger.error('{domain}:{resource}:{operation}:error', { message: error.message }); return handleApiError.call(this, error, 'Failed to execute {operation}'); } } ``` ### 4. Define Operation Descriptions For each operation, create `{operation}.descriptions.ts`: ```typescript // Example for {operation}.descriptions.ts import { INodeProperties } from 'n8n-workflow'; /** * Parameters for {operation} operation */ export const operationDescription: INodeProperties[] = [ { displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false, description: 'Whether to return all results or only up to a given limit', displayOptions: { show: { operation: ['operationName'], resource: ['resourceName'], domain: ['domainName'], }, }, }, { displayName: 'Limit', name: 'limit', type: 'number', default: 50, description: 'Max number of results to return', typeOptions: { minValue: 1, }, displayOptions: { show: { operation: ['operationName'], resource: ['resourceName'], domain: ['domainName'], returnAll: [false], }, }, }, { displayName: 'Additional Fields', name: 'additionalFields', type: 'collection', placeholder: 'Add Field', default: {}, displayOptions: { show: { resource: ['resourceName'], domain: ['domainName'], operation: ['operationName'], }, }, options: [ // Generate from OpenAPI parameters { displayName: 'Parameter Display Name', name: 'parameterName', type: 'string', // Convert OpenAPI type to n8n type default: '', description: 'Parameter description from OpenAPI', }, // Additional fields based on query parameters ], }, // Path parameters as required fields { displayName: 'Required Parameter Name', name: 'requiredParam', type: 'string', required: true, default: '', description: 'Description from OpenAPI', displayOptions: { show: { operation: ['operationName'], resource: ['resourceName'], domain: ['domainName'], }, }, }, ]; ``` ### 5. Implement Resource Files Register operations in `{resource}.methods.ts`: ```typescript // Example for {resource}.methods.ts import { operation1 } from './operations/operation1.methods'; import { operation2 } from './operations/operation2.methods'; // Import other operations export const resourceOperations = { operation1, operation2, // Register other operations }; ``` Define the resource selector in `{resource}.descriptions.ts`: ```typescript // Example for {resource}.descriptions.ts import { INodeProperties } from 'n8n-workflow'; import { addDescriptions } from '../../../helpers/deduplicate'; import { operation1Description } from './operations/operation1.descriptions'; import { operation2Description } from './operations/operation2.descriptions'; // Import other operation descriptions export const resourceOperationSelector: INodeProperties = { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['resourceName'], domain: ['domainName'], }, }, options: [ { name: 'Operation 1 Display Name', value: 'operation1', description: 'Description of operation 1', action: 'Operation 1 action text', }, { name: 'Operation 2 Display Name', value: 'operation2', description: 'Description of operation 2', action: 'Operation 2 action text', }, // Add other operations ], default: 'operation1', }; export const resourceDescriptions: INodeProperties[] = [resourceOperationSelector]; // Add each operation's descriptions addDescriptions(resourceDescriptions, operation1Description); addDescriptions(resourceDescriptions, operation2Description); // Add other operation descriptions ``` ### 6. Implement Domain Files Register resources in `{domain}.operations.ts`: ```typescript // Example for {domain}.operations.ts import { resource1Operations } from './resource1/resource1.methods'; import { resource2Operations } from './resource2/resource2.methods'; // Import other resources export const domainOperations = { resource1: resource1Operations, resource2: resource2Operations, // Register other resources }; ``` Define the domain description in `{domain}.descriptions.ts`: ```typescript // Example for {domain}.descriptions.ts import { INodeProperties } from 'n8n-workflow'; import { resource1Descriptions } from './resource1/resource1.descriptions'; import { resource2Descriptions } from './resource2/resource2.descriptions'; // Import other resource descriptions // Resource selector for this domain export const resourceSelector: INodeProperties = { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, displayOptions: { show: { domain: ['domainName'], }, }, options: [ { name: 'Resource 1', value: 'resource1', description: 'Work with resource 1', }, { name: 'Resource 2', value: 'resource2', description: 'Work with resource 2', }, // Add other resources ], default: 'resource1', }; export const domainDescriptions: INodeProperties[] = [ resourceSelector, ...resource1Descriptions, ...resource2Descriptions, // Include other resource descriptions ]; ``` ### 7. Register in Root Files Update domain in `api/operations.ts`: ```typescript // api/operations.ts import { domainOperations } from './domain/domain.operations'; export const operations = { // Existing domains domainName: domainOperations, }; ``` Update domain in `api/descriptions.ts`: ```typescript // api/descriptions.ts import { INodeProperties } from 'n8n-workflow'; import { domainDescriptions } from './domain/domain.descriptions'; export const allDescriptions: INodeProperties[] = [ // Domain selector // Existing domain descriptions ...domainDescriptions, ]; ``` ## Parameter Type Mapping Convert OpenAPI parameter types to n8n types: | OpenAPI Type | n8n Type | Notes | |--------------|----------|-------| | string | string | | | number/integer | number | | | boolean | boolean | | | array | string | Use with multiOptions=true or comma-separated values | | object | json | | | enum | options | Create options array from enum values | | date/datetime | dateTime | | ## Handling Authentication and Requests Authentication is handled by the `apiRequest` helper which manages OAuth2 tokens: ```typescript // Use the apiRequest helper for all API calls const response = await apiRequest.call( this, 'GET', '/endpoint/path', {}, // body for POST/PUT { // query parameters param1: 'value1', param2: 'value2', } ); ``` ## Error Handling Use the error handler helpers: ```typescript try { // Implementation code } catch (error) { logger.error('domain:resource:operation:error', { message: error.message }); return handleApiError.call(this, error, 'Descriptive error message'); } ``` ## Pagination For list endpoints, use the pagination helper: ```typescript return await handlePagination.call( this, '/endpoint/path', 'GET', {}, // body queryParams, { path: ['data', 'items'], // Where to find items in response fallbackPaths: [['items'], ['data']], // Alternative paths }, returnAll, // Boolean from user input limit, // Number from user input 100 // Batch size ); ``` ## Complete Code Generation When implementing from an OpenAPI spec: 1. Identify all unique paths and operations 2. Group by domain and resource 3. Generate all required files following naming conventions 4. Implement all operations with proper parameter handling 5. Register everything in the correct hierarchy 6. Ensure proper error handling and pagination Your code should handle all edge cases, properly document parameters, and correctly implement the API's behavior.