rawsql-ts
Version:
[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
215 lines • 9.98 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonSchemaValidator = void 0;
class JsonSchemaValidator {
/**
* Validates JsonMapping structure against an expected type structure.
* Checks if the JsonMapping covers all required properties and relationships.
*
* @param jsonMapping The JsonMapping configuration to validate
* @param expectedStructure The expected type structure to validate against
* @returns ValidationResult containing validation status and detailed errors
*/
static validate(jsonMapping, expectedStructure) {
const extractedStructure = this.extractStructureFromJsonMapping(jsonMapping);
return this.compareStructures(extractedStructure, expectedStructure);
}
/**
* Validates JsonMapping structure and throws an error if validation fails.
* Convenience method for strict validation scenarios.
*
* @param jsonMapping The JsonMapping configuration to validate
* @param expectedStructure The expected type structure to validate against
* @throws Error if validation fails with detailed error messages
*/
static validateStrict(jsonMapping, expectedStructure) {
const result = this.validate(jsonMapping, expectedStructure);
if (!result.isValid) {
const errorMessage = [
'JsonMapping validation failed:',
...result.errors
].join('\n');
throw new Error(errorMessage);
}
}
/**
* Extracts structure information from JsonMapping configuration.
* Analyzes rootEntity and nestedEntities to build complete structure map.
*
* @param jsonMapping The JsonMapping to analyze
* @returns ExtractedStructure representing the mapping structure
*/
static extractStructureFromJsonMapping(jsonMapping) {
const structure = {};
// Extract root entity properties
if (jsonMapping.rootEntity && jsonMapping.rootEntity.columns) {
Object.keys(jsonMapping.rootEntity.columns).forEach(propertyName => {
structure[propertyName] = 'primitive';
});
}
// Extract nested entities
if (jsonMapping.nestedEntities) {
// Process direct children of root entity first
jsonMapping.nestedEntities
.filter((entity) => entity.parentId === jsonMapping.rootEntity.id)
.forEach((entity) => {
if (entity.propertyName && entity.columns) {
if (entity.relationshipType === 'object') {
// Single object relationship
structure[entity.propertyName] = this.extractNestedEntityStructure(entity, jsonMapping);
}
else if (entity.relationshipType === 'array') {
// Array relationship
structure[entity.propertyName] = [this.extractNestedEntityStructure(entity, jsonMapping)];
}
}
});
}
return structure;
} /**
* Extracts structure from a nested entity, including its children.
*/
static extractNestedEntityStructure(entity, jsonMapping) {
const entityStructure = {};
// Add entity's own columns
if (entity.columns) {
Object.keys(entity.columns).forEach(propName => {
entityStructure[propName] = 'primitive';
});
}
// Add nested children of this entity
if (jsonMapping.nestedEntities) {
jsonMapping.nestedEntities
.filter((childEntity) => childEntity.parentId === entity.id)
.forEach((childEntity) => {
if (childEntity.propertyName && childEntity.columns) {
if (childEntity.relationshipType === 'object') {
entityStructure[childEntity.propertyName] = this.extractNestedEntityStructure(childEntity, jsonMapping);
}
else if (childEntity.relationshipType === 'array') {
entityStructure[childEntity.propertyName] = [this.extractNestedEntityStructure(childEntity, jsonMapping)];
}
}
});
}
return entityStructure;
} /**
* Compares extracted structure with expected structure with proper type guards.
*/
static compareStructures(extracted, expected, path = '') {
const errors = [];
const missingProperties = [];
const extraProperties = [];
// Handle primitive comparison
if (extracted === 'primitive' && expected === 'primitive') {
return { isValid: true, errors: [], missingProperties: [], extraProperties: [] };
}
// Handle array types
if (Array.isArray(expected) && Array.isArray(extracted)) {
if (expected.length > 0 && extracted.length > 0) {
const nestedResult = this.compareStructures(extracted[0], expected[0], `${path}[]`);
errors.push(...nestedResult.errors);
missingProperties.push(...nestedResult.missingProperties);
extraProperties.push(...nestedResult.extraProperties);
}
return { isValid: errors.length === 0, errors, missingProperties, extraProperties };
} // Both should be objects for property comparison
if (typeof extracted !== 'object' || typeof expected !== 'object' ||
Array.isArray(extracted) || Array.isArray(expected) ||
extracted === null || expected === null) {
return { isValid: true, errors: [], missingProperties: [], extraProperties: [] };
}
// Now we know both are object types, safe to access properties
const extractedObj = extracted;
const expectedObj = expected;
// Check for missing properties in extracted structure
Object.keys(expectedObj).forEach(key => {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in extractedObj)) {
missingProperties.push(currentPath);
errors.push(`Missing property: ${currentPath}`);
return;
}
const extractedValue = extractedObj[key];
const expectedValue = expectedObj[key];
// Recursively compare nested structures
const nestedResult = this.compareStructures(extractedValue, expectedValue, currentPath);
errors.push(...nestedResult.errors);
missingProperties.push(...nestedResult.missingProperties);
extraProperties.push(...nestedResult.extraProperties);
});
// Check for extra properties in extracted structure
Object.keys(extractedObj).forEach(key => {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in expectedObj)) {
extraProperties.push(currentPath);
// Note: Extra properties are not considered errors in this implementation
// as JsonMapping might include additional metadata
}
});
return {
isValid: errors.length === 0,
errors,
missingProperties,
extraProperties
};
}
/**
* Validates JsonMapping structure against a sample object that implements the expected type.
* This method extracts structure from the sample object and compares it with JsonMapping.
*
* @param jsonMapping The JsonMapping configuration to validate
* @param sampleObject A sample object that implements the expected interface/type
* @returns ValidationResult containing validation status and detailed errors
*/
static validateAgainstSample(jsonMapping, sampleObject) {
const expectedStructure = this.extractStructureFromSample(sampleObject);
return this.validate(jsonMapping, expectedStructure);
}
/**
* Validates JsonMapping structure against a sample object and throws an error if validation fails.
* Convenience method for strict validation scenarios with sample objects.
*
* @param jsonMapping The JsonMapping configuration to validate
* @param sampleObject A sample object that implements the expected interface/type
* @throws Error if validation fails with detailed error messages
*/
static validateAgainstSampleStrict(jsonMapping, sampleObject) {
const result = this.validateAgainstSample(jsonMapping, sampleObject);
if (!result.isValid) {
const errorMessage = [
'JsonMapping validation against sample object failed:',
...result.errors
].join('\n');
throw new Error(errorMessage);
}
} /**
* Extracts structure information from a sample object.
* Recursively analyzes the object properties to build a structure map.
*
* @param sampleObject The sample object to analyze
* @returns ExpectedTypeStructure representing the object structure
*/
static extractStructureFromSample(sampleObject) {
if (sampleObject === null || sampleObject === undefined) {
return 'primitive';
}
if (Array.isArray(sampleObject)) {
if (sampleObject.length === 0) {
return [];
}
return [this.extractStructureFromSample(sampleObject[0])];
}
if (typeof sampleObject === 'object') {
const structure = {};
Object.keys(sampleObject).forEach(key => {
structure[key] = this.extractStructureFromSample(sampleObject[key]);
});
return structure;
}
// Primitive types (string, number, boolean, etc.)
return 'primitive';
}
}
exports.JsonSchemaValidator = JsonSchemaValidator;
//# sourceMappingURL=JsonSchemaValidator.js.map
;