Scripting

Best Practices

This guide covers essential best practices for integrating Licentra into your NetSuite solutions, ensuring optimal performance, security, and maintainability.

See More: Licentra Wiki – Modules


Performance Optimization

Batch Feature Checking

Check multiple features in a single script execution when possible.

Code
function checkMultipleFeatures(featureIds) { const results = {}; featureIds.forEach(featureId => { try { results[featureId] = licentraLib.isFeatureEnabled(featureId); } catch (error) { log.error(`Feature Check Failed: ${featureId}`, error.message); results[featureId] = { enabled: false, message: 'Check failed' }; } }); return results; } // Usage const features = ['FEATURE_A', 'FEATURE_B', 'FEATURE_C']; const statuses = checkMultipleFeatures(features);

Lazy Loading

Only check feature status when needed, not on every script execution.

Code
const beforeLoad = (context) => { // Only check features when specific conditions are met if (context.type === context.UserEventType.EDIT) { const status = licentraLib.isFeatureEnabled('EDIT_FEATURES'); if (status.enabled) { addEditFeatures(context.form); } } };

Security Considerations

Input Validation

Always validate feature IDs before passing them to the API.

Code
function validateFeatureId(featureId) { if (!featureId || typeof featureId !== 'string') { throw new Error('Invalid feature ID: must be a non-empty string'); } // Only allow alphanumeric characters and underscores if (!/^[A-Z0-9_]+$/.test(featureId)) { throw new Error('Invalid feature ID: must contain only uppercase letters, numbers, and underscores'); } return featureId; } function safeFeatureCheck(featureId) { try { const validatedId = validateFeatureId(featureId); return licentraLib.isFeatureEnabled(validatedId); } catch (error) { log.error('Feature ID Validation Error', error.message); return { enabled: false, message: 'Invalid feature ID' }; } }

Error Information Disclosure

Be careful not to expose sensitive information in error messages.

Code
function handleFeatureError(error, featureId) { // Log detailed error for debugging log.error('Feature Check Error', { featureId: featureId, error: error.message, stack: error.stack }); // Return generic message to user return { enabled: false, message: 'Feature temporarily unavailable' }; }

Access Control

Implement proper access control for feature-dependent functionality.

Code
function checkUserPermissions(featureId, userId) { try { const status = licentraLib.isFeatureEnabled(featureId); if (!status.enabled) { return false; } // Additional user-specific checks if needed const user = runtime.getCurrentUser(); if (user.role !== 'ADMIN' && featureId.includes('ADMIN_')) { return false; } return true; } catch (error) { log.error('Permission Check Error', error.message); return false; } }

Development Guidelines

Consistent Error Handling

Implement consistent error handling across all feature checks.

Code
class FeatureManager { constructor() { this.cache = new Map(); } isFeatureEnabled(featureId) { try { // Check cache first const cached = this.getCachedStatus(featureId); if (cached) return cached; // Validate input this.validateFeatureId(featureId); // Check with Licentra const status = licentraLib.isFeatureEnabled(featureId); // Cache result this.cacheStatus(featureId, status); return status; } catch (error) { return this.handleError(error, featureId); } } validateFeatureId(featureId) { if (!featureId || typeof featureId !== 'string') { throw new Error('Invalid feature ID'); } } handleError(error, featureId) { log.error('Feature Check Error', { featureId: featureId, error: error.message }); return { enabled: false, message: 'Feature check failed' }; } getCachedStatus(featureId) { const cached = this.cache.get(featureId); if (cached && (Date.now() - cached.timestamp) < 300000) { return cached.status; } return null; } cacheStatus(featureId, status) { this.cache.set(featureId, { status: status, timestamp: Date.now() }); } } // Usage const featureManager = new FeatureManager(); const status = featureManager.isFeatureEnabled('MY_FEATURE');

Logging Standards

Implement comprehensive logging for debugging and monitoring.

Code
function logFeatureCheck(featureId, status, context = {}) { const logData = { featureId: featureId, enabled: status.enabled, message: status.message, timestamp: new Date().toISOString(), scriptType: context.scriptType || 'unknown', userId: context.userId || 'unknown', ...context }; if (status.enabled) { log.debug('Feature Check Success', logData); } else { log.audit('Feature Check Disabled', logData); } } // Usage const status = licentraLib.isFeatureEnabled('FEATURE_ID'); logFeatureCheck('FEATURE_ID', status, { scriptType: 'UserEvent', userId: runtime.getCurrentUser().id });

Configuration Management

Centralize feature configuration for easier maintenance.

Code
const FEATURE_CONFIG = { 'BASIC_FEATURES': { description: 'Basic functionality', fallback: true, cacheTimeout: 300000 }, 'ADVANCED_FEATURES': { description: 'Advanced functionality', fallback: false, cacheTimeout: 600000 }, 'ADMIN_FEATURES': { description: 'Administrative features', fallback: false, cacheTimeout: 300000, requireAdmin: true } }; function getFeatureConfig(featureId) { return FEATURE_CONFIG[featureId] || { description: 'Unknown feature', fallback: false, cacheTimeout: 300000 }; }

By following these best practices, you'll create robust, maintainable, and performant Licentra integrations that provide excellent user experience while ensuring security and reliability.

Last modified on