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 .
- 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
helperslibrary -
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:
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:
{
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: "<p>[default]</p>"
},
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:
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:
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.
-
Returns:
-
helpers.isNotEmpty(value)-
Returns:
boolean - Usage: Checks if a value is not null, undefined, empty string, empty array, or empty object.
-
Returns:
-
helpers.isEmpty(value)-
Returns:
boolean -
Usage:
The inverse of
isNotEmpty.
-
Returns:
-
helpers.isArray(value)-
Returns:
boolean - Usage: Validates if a value is an array.
-
Returns:
-
helpers.isString(value)-
Returns:
boolean
-
Returns:
-
helpers.isNumber(value)-
Returns:
boolean
-
Returns:
5. console - Logging
Use console.log() to write messages to the system logs. This is essential for debugging hooks or logging specific validation decisions.
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.
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.
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.
// 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.
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 accessarticle.metadata.descriptiondirectly without checking ifarticle.metadataexists first. -
Clear Error Messages:
When returning
success: false, provide a descriptiveerrorstring and use thedetailsobject to pass relevant data (like lists of failed article names). -
Logging:
Use
console.loggenerously. These logs are saved with the job history and are vital for troubleshooting why a hook failed a package.