rawsql-ts
Version:
[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
115 lines • 5.43 kB
JavaScript
/**
* Model-driven JSON mapping structure that mirrors TypeScript model definitions.
* This approach provides intuitive, hierarchical mapping that closely resembles the target data structure.
*/
/**
* Convert a model-driven JSON mapping to the traditional JsonMapping format.
* This enables backward compatibility with existing PostgresJsonQueryBuilder.
*/
export function convertModelDrivenMapping(modelMapping) {
const protectedStringFields = [];
let entityIdCounter = 0;
const propertyNameCounters = {};
// Generate unique entity IDs
const generateEntityId = () => `entity_${++entityIdCounter}`;
// Generate unique property names to avoid JSON key conflicts
const generateUniquePropertyName = (baseName) => {
if (!propertyNameCounters[baseName]) {
propertyNameCounters[baseName] = 0;
}
propertyNameCounters[baseName]++;
return propertyNameCounters[baseName] === 1 ? baseName : `${baseName}_${propertyNameCounters[baseName]}`;
};
// Helper function to process structure fields and extract entities
const processStructure = (structure, parentId = null) => {
const columns = {};
const nestedEntities = [];
for (const [fieldName, config] of Object.entries(structure)) {
if (typeof config === 'string') {
// Simple field mapping: "fieldName": "column_name"
columns[fieldName] = config;
}
else if ('column' in config && typeof config.column === 'string' && !('type' in config && (config.type === 'object' || config.type === 'array'))) {
// Enhanced field mapping: "fieldName": { "column": "column_name", "type": "string" }
const fieldConfig = config;
if (typeof fieldConfig === 'object' && 'column' in fieldConfig) {
columns[fieldName] = fieldConfig.column;
if (fieldConfig.type === 'string') {
protectedStringFields.push(fieldConfig.column);
}
}
}
else if ('from' in config && typeof config.from === 'string' && !('type' in config && (config.type === 'object' || config.type === 'array'))) {
// Legacy field mapping: "fieldName": { "from": "column_name", "type": "string" }
const fieldConfig = config;
if (typeof fieldConfig === 'object' && 'from' in fieldConfig) {
columns[fieldName] = fieldConfig.from;
if (fieldConfig.type === 'string') {
protectedStringFields.push(fieldConfig.from);
}
}
}
else if ('type' in config && (config.type === 'object' || config.type === 'array')) {
// Nested structure: object or array
const nestedStructure = config;
const uniquePropertyName = generateUniquePropertyName(fieldName);
// Generate globally unique entity ID to ensure unique JSON column names
const entityId = generateEntityId();
const processedNested = processStructure(nestedStructure.structure, entityId);
nestedEntities.push({
id: entityId, // Use unique ID to avoid column conflicts
name: fieldName.charAt(0).toUpperCase() + fieldName.slice(1), // Capitalize first letter
parentId: parentId || 'root',
propertyName: uniquePropertyName,
originalPropertyName: fieldName, // Store original name for final mapping
relationshipType: nestedStructure.type,
columns: processedNested.columns
});
// Add nested entities from deeper levels
nestedEntities.push(...processedNested.nestedEntities.map(entity => (Object.assign(Object.assign({}, entity), { parentId: entity.parentId === 'root' ? entityId : entity.parentId }))));
}
}
return { columns, nestedEntities };
};
// Process the root structure
const processed = processStructure(modelMapping.structure); // Build the traditional JsonMapping
const jsonMapping = {
rootName: 'root', // Default root name
rootEntity: {
id: 'root',
name: 'Root',
columns: processed.columns
},
nestedEntities: processed.nestedEntities
};
// Add typeInfo for backward compatibility
jsonMapping.typeInfo = modelMapping.typeInfo;
return {
jsonMapping,
typeProtection: { protectedStringFields }
};
}
/**
* Validate that a model-driven mapping structure is well-formed.
*/
export function validateModelDrivenMapping(mapping) {
const errors = [];
// Validate typeInfo
if (!mapping.typeInfo) {
errors.push('typeInfo is required');
}
else {
if (!mapping.typeInfo.interface) {
errors.push('typeInfo.interface is required');
}
if (!mapping.typeInfo.importPath) {
errors.push('typeInfo.importPath is required');
}
}
// Validate structure
if (!mapping.structure || typeof mapping.structure !== 'object') {
errors.push('structure is required and must be an object');
}
return errors;
}
//# sourceMappingURL=ModelDrivenJsonMapping.js.map