UNPKG

@gsb-core/core

Version:

GSB core services and classes for platform-independent web applications

266 lines 13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EntityDefValidatorService = void 0; const gsb_entity_def_model_1 = require("../../models/gsb-entity-def.model"); const gsb_entity_service_1 = require("./gsb-entity.service"); const query_params_1 = require("../../types/query-params"); const gsb_config_1 = require("../../config/gsb-config"); const gsb_utils_1 = require("../../utils/gsb-utils"); const gsb_cache_service_1 = require("../cache/gsb-cache.service"); /** * Service for validating Entity Definitions and Properties * Separates validation logic from the main EntityDefService */ class EntityDefValidatorService { constructor() { this.entityService = gsb_entity_service_1.GsbEntityService.getInstance(); } static getInstance() { if (!EntityDefValidatorService.instance) { EntityDefValidatorService.instance = new EntityDefValidatorService(); } return EntityDefValidatorService.instance; } /** * Validates that entity definition contains required properties and has valid formatting * @param entityDef The entity definition to validate * @param isCreate Whether this is a create operation (stricter validation) * @throws Error if validation fails with descriptive message */ validateEntityDef(entityDef, isCreate = true) { if (isCreate) { // Name validation if (!entityDef.name) { throw new Error('Entity definition must have a name'); } // Title validation if (!entityDef.title) { entityDef.title = entityDef.name; } // Properties validation - only check for create operations or if properties are provided if ((!entityDef.properties || entityDef.properties.length === 0)) { throw new Error('Entity definition must have at least one property'); } } // Check if name is alphanumeric and starts with a letter if (entityDef.name && !/^[a-zA-Z][a-zA-Z0-9_]*$/.test(entityDef.name)) { throw new Error('Entity name must start with a letter and contain only alphanumeric characters or underscores'); } // Validate each property if provided if (entityDef.properties && entityDef.properties.length > 0) { for (const property of entityDef.properties) { this.validateProperty(property, isCreate); } } } /** * Validates a single property * @param property The property to validate * @param isCreate Whether this is a create operation (stricter validation) * @throws Error if validation fails with descriptive message */ validateProperty(property, isCreate = true) { if (isCreate) { // Validate property name if (!property.name) { throw new Error('Each property must have a name'); } // Validate property definition ID - required for all operations if (!property.definition_id) { throw new Error(`Property '${property.name}' must have a definition_id`); } // Validate reference properties if (property.definition_id === gsb_utils_1.GsbUtils.ReferencePropertyDefinitionId) { if (!property.refEntDef_id) { throw new Error(`Reference property '${property.name}' must have refEntDef_id`); } if (!property.refEntPropName) { throw new Error(`Reference property '${property.name}' must have refEntPropName`); } // Check if refEntDef_id is a valid GUID if (!/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(property.refEntDef_id)) { throw new Error(`Property '${property.name}' has an invalid refEntDef_id format`); } } // Validate enum properties - only required for create or if definition_id is for enum if (property.definition_id === '7bf08f4f-7de0-469e-bbfb-f4c43762f4d7' && !property.enum_id) { throw new Error(`Enum property '${property.name}' must have an enum_id`); } } // Check property name format (alphanumeric, starts with letter, can have underscore) if (property.name && !/^[a-zA-Z][a-zA-Z0-9_]*$/.test(property.name)) { throw new Error(`Property name '${property.name}' must start with a letter and contain only alphanumeric characters or underscores`); } } /** * Normalize and generate missing values for the entity definition * @param entityDef The entity definition to normalize * @returns A normalized entity definition with all required fields */ normalizeEntityDef(entityDef, tenantCode) { var _a, _b, _c, _d; // Clone to avoid modifying the original const normalizedDef = { ...entityDef }; // Generate dbTableName if not provided if (!normalizedDef.dbTableName) { // Generate a unique suffix (5 alphanumeric chars) let uniqueSuffix = Math.random().toString(36).substring(2, 7); normalizedDef.dbTableName = `${(_a = normalizedDef.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()}${uniqueSuffix}`; // +4 random alphanumeric chars } // Ensure all properties have titles if not provided if (normalizedDef.properties) { normalizedDef.properties = normalizedDef.properties.map(prop => this.normalizeProperty(prop)); } // Set default active and deleted flags normalizedDef.undeleteable = (_b = normalizedDef.undeleteable) !== null && _b !== void 0 ? _b : true; normalizedDef.variationType = (_c = normalizedDef.variationType) !== null && _c !== void 0 ? _c : 2; normalizedDef.activityLogLevel = (_d = normalizedDef.activityLogLevel) !== null && _d !== void 0 ? _d : gsb_entity_def_model_1.ActivityLogLevel.Delete; if (!normalizedDef.module_id) { normalizedDef.module = { name: tenantCode, title: tenantCode, code: tenantCode }; } return normalizedDef; } /** * Normalize a property with sensible defaults * @param property The property to normalize * @returns Normalized property with defaults applied */ normalizeProperty(property) { const normalizedProp = { ...property }; // Generate title from name if not provided if (!normalizedProp.title && normalizedProp.name) { // Convert camelCase to Title Case (e.g., "firstName" to "First Name") normalizedProp.title = normalizedProp.name .replace(/([A-Z])/g, ' $1') .replace(/^./, str => str.toUpperCase()); } else if (normalizedProp.title && !normalizedProp.name) { normalizedProp.name = gsb_utils_1.GsbUtils.toCamelCase(normalizedProp.title); } // Set appropriate default values if (normalizedProp.isSearchable === undefined) { // Default searchable for string properties if (normalizedProp.definition_id === gsb_utils_1.GsbUtils.StringPropertyDefinitionId) { normalizedProp.isSearchable = true; } } if (property.listScreens === undefined) property.listScreens = gsb_entity_def_model_1.ScreenType.All; if (property.formModes === undefined) property.formModes = 66049; return normalizedProp; } /** * Check if entity name is unique * @param nameToCheck The name to check for uniqueness * @param currentId Optional ID of the entity being updated (to exclude from check) * @param token Optional custom token to use for this request * @param tenantCode Optional custom tenant code to use for this request * @returns Promise resolving to a boolean indicating if the name is unique */ async isEntityNameUnique(nameToCheck, currentId, token, tenantCode) { try { const query = new query_params_1.QueryParams('GsbEntityDef'); query.filter('name', nameToCheck); query.select(['id', 'name']); const response = await this.entityService.query(query, token || (0, gsb_config_1.getGsbToken)(), tenantCode || (0, gsb_config_1.getGsbTenantCode)()); if (!response.entities || response.entities.length === 0) { return true; } // If we're updating an existing entity, it's ok if we find only that entity if (currentId && response.entities.length === 1 && response.entities[0].id === currentId) { return true; } return false; } catch (error) { console.error('Error checking entity name uniqueness:', error); throw new Error('Failed to check entity name uniqueness'); } } /** * Check if database table name is unique * @param tableNameToCheck The table name to check for uniqueness * @param currentId Optional ID of the entity being updated (to exclude from check) * @param token Optional custom token to use for this request * @param tenantCode Optional custom tenant code to use for this request * @returns Promise resolving to a boolean indicating if the table name is unique */ async isTableNameUnique(tableNameToCheck, currentId, token, tenantCode) { try { const query = new query_params_1.QueryParams('GsbEntityDef'); query.filter('dbTableName', tableNameToCheck); query.select(['id', 'dbTableName']); const response = await this.entityService.query(query, token || (0, gsb_config_1.getGsbToken)(), tenantCode || (0, gsb_config_1.getGsbTenantCode)()); if (!response.entities || response.entities.length === 0) { return true; } // If we're updating an existing entity, it's ok if we find only that entity if (currentId && response.entities.length === 1 && response.entities[0].id === currentId) { return true; } return false; } catch (error) { console.error('Error checking table name uniqueness:', error); throw new Error('Failed to check table name uniqueness'); } } /** * Check if reference property name is already used in the referenced entity * @param refPropName Reference property name to check * @param refEntityId ID of the referenced entity * @param propertyId Optional ID of the property being updated (to exclude from check) * @param token Optional custom token to use for this request * @param tenantCode Optional custom tenant code to use for this request * @returns Promise with validation result */ async isRefPropNameUnique(refPropName, refEntityId, propertyId, token, tenantCode) { if (!refPropName || !refEntityId) { return true; } try { const query = new query_params_1.QueryParams("GsbProperty"); query.filter('ownerEntDef_id', refEntityId); query.filter('name', refPropName); query.select(['id', 'name']); const response = await this.entityService.query(query, token || (0, gsb_config_1.getGsbToken)(), tenantCode || (0, gsb_config_1.getGsbTenantCode)()); if (!response.entities || response.entities.length === 0) { return true; } // If we're updating an existing property, it's ok if we find only that property if (propertyId && response.entities.length === 1 && response.entities[0].id === propertyId) { return true; } return false; } catch (error) { console.error("Error checking reference property name:", error); throw new Error('Failed to check reference property name uniqueness'); } } /** * Validate property name uniqueness within an entity * @param propertyName Property name to check * @param entityProperties Existing properties in the entity * @param propertyId Optional ID of the property being updated (to exclude from check) * @returns True if the property name is unique */ async isPropertyNameUnique(entityDef, propertyName, propertyId) { let cache = await gsb_cache_service_1.GsbCacheService.getInstance().getEntityDef(entityDef); if (!cache) { return true; } if (!propertyName || !cache.properties || cache.properties.length === 0) { return true; } const existingProperty = cache.properties.find(p => p.name === propertyName && (!propertyId || p.id !== propertyId)); return !existingProperty; } } exports.EntityDefValidatorService = EntityDefValidatorService; //# sourceMappingURL=entity-def-validator.service.js.map