@gsb-core/core
Version:
GSB core services and classes for platform-independent web applications
266 lines • 13.2 kB
JavaScript
;
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