# Custom Validation Hook Context This document describes the objects, data structures, and utilities available to you when writing custom pre-validation and post-validation hooks. Use this reference to implement custom business logic that executes during the content ingestion process. Hooks run in a secure, sandboxed JavaScript environment. When your hook executes, the system injects specific objects into the global scope, allowing you to inspect the data package, review job metadata, and report validation results. ## Prerequisites Before writing custom hooks, ensure you are familiar with: - **JavaScript (ES5/ES6):** Hooks are written in standard JavaScript. - **Data Import Format:** Understanding the structure of articles, folders, and topics as defined in the [Format Guide](/developer-portal/guides/ingestion/data-import-format-guide). - **Hook Architecture:** Whether you are writing a *Pre-Validation* hook (runs before standard checks) or a *Post-Validation* hook (runs after standard checks). ## Security and Environment Restrictions Your code executes in a restricted sandbox. For security reasons, the following capabilities are **NOT available**: - File system access (`fs`, `__dirname`, `__filename`) - Network access (HTTP requests) - Module loading (`require`) - Global process access (`process`, `global`) - `eval()` or dynamic code execution **Allowed Capabilities:** - Accessing injected objects (`data`, `metadata`, `validationResults`) - Using the provided `helpers` library - Using `console.log()` for system logging - Standard JavaScript logic (loops, conditionals, array manipulation) ## Available Objects The following objects are injected into your hook's scope at runtime. ### 1. `data` - Package Content The `data` object represents the parsed content from the package being imported. You can inspect this object to validate naming conventions, folder structures, or content requirements. **Structure Overview:** ```javascript data = { articles: [], // Array of article objects folders: [], // Array of folder objects portals: [], // Array of portal objects topics: [], // Array of topic objects macros: [] // Array of macro objects } ``` **Article Object Model:** Each item in the `articles` array contains the following properties: ```javascript { name: "article-name", // Required: Article name folder: { path: "Articles/Content/Shared" // Required: Full folder path }, content: "content/article.html", // Path to file OR direct HTML string ai: "true", // String "true"/"false" availabilityDate: "12-22-2024", // Optional metadata: { description: "Sample Article", keywords: "Chat", additionalInfo: "Info" }, articleMacro: { // Present only if article is a Macro name: "CompanySize", defaultValue: "
[default]
" }, customAttributes: [ { name: "attribute1", value: "value1" } ], // ... various optional fields (classifications, relatedArticles, etc.) } ``` ### 2. `metadata` - Job Context The `metadata` object provides context about the current execution environment and the job itself. **Structure:** ```javascript metadata = { packageId: "package-123", // Unique Job ID domain: "example.com", // Tenant Domain tenantId: "tenant-456", // Tenant ID departmentName: "Support" // (Import workflow only) } ``` ### 3. `validationResults` - Standard Validation Output **Note:** This object is available **ONLY** in Post-Validation hooks. This object contains the results of the standard system validation that ran immediately before your hook. You can use this to determine if you should block an import based on specific errors. **Structure:** ```javascript validationResults = { success: false, summary: { totalArticles: 100, totalErrors: 7, invalidAttachments: 5 }, errors: [ { type: "attachment_missing", articleName: "article-1", message: "Attachment 'image.png' not found", fix: "Ensure attachment exists in resources folder" } ], warnings: [] } ``` ### 4. `helpers` - Utility Library The `helpers` object provides safe utility functions to handle data checking without throwing runtime errors on undefined properties. **Available Functions:** * **`helpers.hasField(obj, field)`** * *Returns:* `boolean` * *Usage:* Checks if an object has a specific property. * **`helpers.isNotEmpty(value)`** * *Returns:* `boolean` * *Usage:* Checks if a value is not null, undefined, empty string, empty array, or empty object. * **`helpers.isEmpty(value)`** * *Returns:* `boolean` * *Usage:* The inverse of `isNotEmpty`. * **`helpers.isArray(value)`** * *Returns:* `boolean` * *Usage:* Validates if a value is an array. * **`helpers.isString(value)`** * *Returns:* `boolean` * **`helpers.isNumber(value)`** * *Returns:* `boolean` ### 5. `console` - Logging Use `console.log()` to write messages to the system logs. This is essential for debugging hooks or logging specific validation decisions. ```javascript console.log('Processing package:', metadata.packageId); console.log('Found error in article:', article.name); ``` ## Execution Contexts Depending on when your hook runs, different objects are available. ### Pre-Validation Hooks **When:** Executes **BEFORE** the standard system validation. **Goal:** Enforce custom business rules (e.g., "All articles must have a keyword" or "AI must be enabled"). - ✅ `data` - ✅ `metadata` - ✅ `helpers` / `console` - ❌ `validationResults` (Not available yet) ### Post-Validation Hooks **When:** Executes **AFTER** the standard system validation. **Goal:** Review system errors and decide if the job should proceed (e.g., "Fail the job if any image attachments are missing"). - ✅ `data` - ✅ `metadata` - ✅ `validationResults` - ✅ `helpers` / `console` ## Return Value Format Your hook **must** return a `result` object. This object dictates whether the validation process considers the job a success or failure. ### Success Scenario Return this to allow the job to proceed. ```javascript var result = { success: true, message: "Custom validation passed." }; return result; ``` ### Failure Scenario Return this to stop the job and report an error to the user. ```javascript var result = { success: false, error: "Critical Business Rule Failed: Articles must have an author.", details: { failedCount: 5, articleNames: ["intro", "setup", "config"] } }; return result; ``` ## Example: Pre-Validation Logic This example demonstrates checking that every article contains a specific metadata field. ```javascript // Initialize result var result = { success: true }; // Verify data exists if (helpers.hasField(data, 'articles') && helpers.isNotEmpty(data.articles)) { var invalidArticles = []; // Loop through articles for (var i = 0; i < data.articles.length; i++) { var article = data.articles[i]; // Check for "name" field if (!helpers.hasField(article, 'name')) { invalidArticles.push({ index: i, issue: "Missing name" }); continue; } // Custom Rule: Check if 'description' exists in metadata if (!helpers.hasField(article, 'metadata') || !helpers.hasField(article.metadata, 'description') || helpers.isEmpty(article.metadata.description)) { invalidArticles.push({ name: article.name, issue: "Missing required description metadata" }); } } // If we found issues, fail the job if (invalidArticles.length > 0) { result = { success: false, error: 'Articles failed custom metadata validation', details: { count: invalidArticles.length, errors: invalidArticles } }; } } // Return the result object result; ``` ## Example: Post-Validation Logic This example checks the standard validation results. If there are standard errors, it logs them and fails the job explicitly. ```javascript var result = { success: true }; // Check if standard validation found errors if (helpers.hasField(validationResults, 'errors') && validationResults.errors.length > 0) { var errorCount = validationResults.errors.length; console.log('Standard validation found ' + errorCount + ' errors.'); // Create a summary of error types var errorTypes = {}; validationResults.errors.forEach(function(err) { var type = err.type || 'unknown'; errorTypes[type] = (errorTypes[type] || 0) + 1; }); // Fail the job result = { success: false, error: 'Standard validation failed with ' + errorCount + ' errors.', details: { summary: errorTypes, firstError: validationResults.errors[0].message } }; } result; ``` ## Best Practices - **Defensive Coding:** Always use `helpers.hasField()` before accessing nested properties. For example, do not access `article.metadata.description` directly without checking if `article.metadata` exists first. - **Clear Error Messages:** When returning `success: false`, provide a descriptive `error` string and use the `details` object to pass relevant data (like lists of failed article names). - **Logging:** Use `console.log` generously. These logs are saved with the job history and are vital for troubleshooting why a hook failed a package.