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
Markdown
# 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.