revit-cli
Version:
A scalable CLI tool for Revit communication and data manipulation
472 lines • 17.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RevitConnector = void 0;
const axios_1 = __importDefault(require("axios"));
const logger_1 = require("../utils/logger");
/**
* Handles communication with Revit API
*/
class RevitConnector {
constructor() {
this.isConnected = false;
this.logger = new logger_1.Logger();
// Initialize axios client with default config
this.client = axios_1.default.create({
timeout: 30000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
this.setupInterceptors();
}
/**
* Setup request/response interceptors for logging and error handling
*/
setupInterceptors() {
// Request interceptor
this.client.interceptors.request.use((config) => {
this.logger.debug('=== HTTP REQUEST START ===');
this.logger.debug(`Making request to: ${config.method?.toUpperCase()} ${config.url}`);
this.logger.debug('Request config:', {
method: config.method,
url: config.url,
baseURL: config.baseURL,
timeout: config.timeout,
headers: config.headers,
data: config.data
});
this.logger.debug('Full request URL:', `${config.baseURL || ''}${config.url}`);
return config;
}, (error) => {
this.logger.error('=== HTTP REQUEST ERROR ===');
this.logger.error('Request setup error:', {
message: error.message,
code: error.code,
config: error.config
});
return Promise.reject(error);
});
// Response interceptor
this.client.interceptors.response.use((response) => {
this.logger.debug('=== HTTP RESPONSE SUCCESS ===');
this.logger.debug(`Response received: ${response.status} ${response.statusText}`);
this.logger.debug('Response details:', {
status: response.status,
statusText: response.statusText,
headers: response.headers,
dataType: typeof response.data,
dataLength: response.data ? JSON.stringify(response.data).length : 0
});
if (response.data && typeof response.data === 'object') {
this.logger.debug('Response data sample:', JSON.stringify(response.data).substring(0, 500));
}
return response;
}, (error) => {
this.logger.error('=== HTTP RESPONSE ERROR ===');
this.logger.error('Response error details:', {
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText,
responseData: error.response?.data,
requestURL: error.config?.url,
requestMethod: error.config?.method
});
if (error.response) {
this.logger.error('Error response body:', error.response.data);
}
return Promise.reject(error);
});
}
/**
* Initialize the connector with configuration
*/
async initialize(configManager) {
const config = configManager.getConfig();
// Update client configuration
if (config.revit?.apiUrl) {
this.client.defaults.baseURL = config.revit.apiUrl;
}
if (config.revit?.timeout) {
this.client.defaults.timeout = config.revit.timeout;
}
this.logger.debug('Revit Connector initialized');
}
/**
* Test connection to Revit API
*/
async testConnection() {
try {
this.logger.debug('=== CONNECTION TEST START ===');
this.logger.debug('Testing connection to:', this.client.defaults.baseURL);
const response = await this.client.get('/health');
this.logger.debug('Health check response:', {
status: response.status,
statusText: response.statusText,
data: response.data
});
this.isConnected = response.status === 200;
if (this.isConnected) {
this.logger.info('Successfully connected to Revit API');
}
else {
this.logger.warn('Revit API health check returned non-200 status');
}
this.logger.debug('Connection test result:', this.isConnected);
this.logger.debug('=== CONNECTION TEST END ===');
return this.isConnected;
}
catch (error) {
this.logger.error('=== CONNECTION TEST ERROR ===');
this.isConnected = false;
this.logger.error('Failed to connect to Revit API:', {
message: error instanceof Error ? error.message : String(error),
code: error?.code,
status: error?.response?.status
});
return false;
}
}
/**
* Get Revit project information
*/
async getProjectInfo() {
try {
const response = await this.client.get('/project/info');
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError('Failed to get project info', error);
}
}
/**
* Extract elements from Revit based on options
*/
async extractElements(options) {
try {
this.logger.debug('=== EXTRACT ELEMENTS START ===');
this.logger.debug('Extract options:', options);
this.logger.debug('Client base URL:', this.client.defaults.baseURL);
this.logger.debug('Client timeout:', this.client.defaults.timeout);
this.logger.debug('Making POST request to /elements/extract');
const response = await this.client.post('/elements/extract', options);
this.logger.debug('Extract response received:', {
status: response.status,
dataType: typeof response.data,
dataLength: response.data ? JSON.stringify(response.data).length : 0
});
this.logger.debug('=== EXTRACT ELEMENTS END ===');
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
this.logger.error('=== EXTRACT ELEMENTS ERROR ===');
return this.handleError('Failed to extract elements', error);
}
}
/**
* Get elements by category
*/
async getElementsByCategory(category) {
try {
const response = await this.client.get(`/elements/category/${encodeURIComponent(category)}`);
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to get elements for category: ${category}`, error);
}
}
/**
* Get element by ID
*/
async getElementById(elementId) {
try {
const response = await this.client.get(`/elements/${encodeURIComponent(elementId)}`);
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to get element: ${elementId}`, error);
}
}
/**
* Get all available categories
*/
async getCategories() {
try {
this.logger.debug('=== GET CATEGORIES START ===');
this.logger.debug('Fetching all available categories from Revit API');
this.logger.debug('Client base URL:', this.client.defaults.baseURL);
this.logger.debug('Client timeout:', this.client.defaults.timeout);
this.logger.debug('Current headers:', this.client.defaults.headers);
const startTime = Date.now();
this.logger.debug('Making GET request to /categories endpoint');
const response = await this.client.get('/categories');
const duration = Date.now() - startTime;
this.logger.debug(`Categories request completed in ${duration}ms`);
this.logger.debug('Categories response received:', {
status: response.status,
statusText: response.statusText,
dataType: typeof response.data,
isArray: Array.isArray(response.data),
dataLength: response.data ? JSON.stringify(response.data).length : 0,
headers: response.headers
});
// Validate response data structure
if (!response.data) {
this.logger.warn('Response data is null or undefined');
return {
success: true,
data: [],
timestamp: new Date().toISOString()
};
}
let categories = [];
// Handle different response structures
if (Array.isArray(response.data)) {
// Direct array response
categories = response.data;
this.logger.debug('Response is direct array format');
}
else if (typeof response.data === 'object') {
// Check for nested structure with 'data' property
if ('data' in response.data && Array.isArray(response.data.data)) {
categories = response.data.data;
this.logger.debug('Found categories in response.data.data structure');
}
// Check for nested structure with 'categories' property
else if ('categories' in response.data && Array.isArray(response.data.categories)) {
categories = response.data.categories;
this.logger.debug('Found categories in response.data.categories structure');
}
// If response.data is an object but not nested, treat it as a single category
else if (response.data.id && response.data.name) {
categories = [response.data];
this.logger.debug('Response is single category object');
}
else {
this.logger.warn('Unknown response structure:', response.data);
}
}
if (categories.length > 0) {
this.logger.debug(`Successfully retrieved ${categories.length} categories`);
this.logger.debug('Sample category:', categories[0]);
if (categories.length <= 5) {
this.logger.debug('All categories:', categories);
}
else {
this.logger.debug('First 3 categories:', categories.slice(0, 3));
}
}
else {
this.logger.warn('No categories found in response');
}
this.logger.debug('=== GET CATEGORIES SUCCESS ===');
return {
success: true,
data: categories,
timestamp: new Date().toISOString()
};
}
catch (error) {
this.logger.error('=== GET CATEGORIES ERROR ===');
this.logger.error('Failed to get categories:', {
message: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
url: `${this.client.defaults.baseURL}/categories`
});
// Check if it's a connection error
if (error instanceof Error && (error.message.includes('ECONNREFUSED') || error.message.includes('Network Error'))) {
this.logger.error('Connection refused - is Revit API Server running?');
this.logger.error('Please ensure:');
this.logger.error('1. Revit is running');
this.logger.error('2. The Revit API Server add-in is loaded');
this.logger.error('3. The server is listening on the configured port');
}
return this.handleError('Failed to get categories', error);
}
}
/**
* Get parameters for a specific category
*/
async getCategoryParameters(category) {
try {
const response = await this.client.get(`/categories/${encodeURIComponent(category)}/parameters`);
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to get parameters for category: ${category}`, error);
}
}
/**
* Execute a custom Revit API command
*/
async executeCommand(command, parameters) {
try {
const response = await this.client.post('/commands/execute', {
command,
parameters: parameters || {}
});
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to execute command: ${command}`, error);
}
}
/**
* Export data in specified format
*/
async exportData(data, format = 'json') {
try {
const response = await this.client.post('/export', {
data,
format
});
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to export data as ${format}`, error);
}
}
/**
* Import data from file
*/
async importData(filePath, format = 'json') {
try {
const response = await this.client.post('/import', {
filePath,
format
});
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError(`Failed to import data from ${filePath}`, error);
}
}
/**
* Get Revit application status
*/
async getStatus() {
try {
const response = await this.client.get('/status');
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError('Failed to get Revit status', error);
}
}
/**
* Handle API errors consistently
*/
handleError(message, error) {
const errorMessage = error.response?.data?.message || error.message || 'Unknown error';
this.logger.error(`${message}: ${errorMessage}`);
return {
success: false,
error: errorMessage,
timestamp: new Date().toISOString()
};
}
/**
* Make a custom API request
*/
async request(config) {
try {
const response = await this.client.request(config);
return {
success: true,
data: response.data,
timestamp: new Date().toISOString()
};
}
catch (error) {
return this.handleError('Custom API request failed', error);
}
}
/**
* Check if connector is connected
*/
async getConnectionStatus() {
this.logger.debug('=== GET CONNECTION STATUS ===');
this.logger.debug('Testing actual connection to Revit API...');
this.logger.debug('Client config:', {
baseURL: this.client.defaults.baseURL,
timeout: this.client.defaults.timeout
});
try {
// Actually test the connection by calling the health endpoint
const isConnected = await this.testConnection();
if (isConnected) {
this.logger.debug('Connection status check: SUCCESS');
return { connected: true };
}
else {
this.logger.debug('Connection status check: FAILED - Health check returned false');
return { connected: false, error: 'Health check failed' };
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.debug('Connection status check: ERROR -', errorMessage);
return { connected: false, error: errorMessage };
}
}
/**
* Set custom headers for API requests
*/
setHeaders(headers) {
Object.assign(this.client.defaults.headers, headers);
}
/**
* Set authentication token
*/
setAuthToken(token) {
this.client.defaults.headers['Authorization'] = `Bearer ${token}`;
}
/**
* Clear authentication
*/
clearAuth() {
delete this.client.defaults.headers['Authorization'];
}
}
exports.RevitConnector = RevitConnector;
//# sourceMappingURL=revit-connector.js.map