@api-client/core 0.18.13 → 0.18.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/modeling/DomainValidation.d.ts +78 -0
- package/build/src/modeling/DomainValidation.d.ts.map +1 -1
- package/build/src/modeling/DomainValidation.js +78 -0
- package/build/src/modeling/DomainValidation.js.map +1 -1
- package/build/src/modeling/validation/entity_validation.d.ts +17 -3
- package/build/src/modeling/validation/entity_validation.d.ts.map +1 -1
- package/build/src/modeling/validation/entity_validation.js +51 -10
- package/build/src/modeling/validation/entity_validation.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +6 -6
- package/package.json +1 -1
- package/src/modeling/DomainValidation.ts +78 -0
- package/src/modeling/validation/entity_validation.ts +59 -10
- package/tests/unit/modeling/validation/entity_validation.spec.ts +95 -0
|
@@ -1,8 +1,86 @@
|
|
|
1
1
|
import { DataDomain } from './DataDomain.js';
|
|
2
2
|
import { DomainImpactReport } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* DomainValidation performs comprehensive validation on a data domain and its components.
|
|
5
|
+
*
|
|
6
|
+
* This class orchestrates validation across all domain elements including entities, properties,
|
|
7
|
+
* associations, and semantics. It ensures that the data domain is well-formed and follows
|
|
8
|
+
* established conventions before it can be published or used in API generation.
|
|
9
|
+
*
|
|
10
|
+
* ## Validation Scope
|
|
11
|
+
*
|
|
12
|
+
* The validation process covers:
|
|
13
|
+
* - **Entities**: Structure, naming, primary keys, and inheritance
|
|
14
|
+
* - **Properties**: Naming conventions, data types, and constraints
|
|
15
|
+
* - **Associations**: Relationships, targets, and naming
|
|
16
|
+
* - **Semantics**: Recommended patterns and best practices
|
|
17
|
+
*
|
|
18
|
+
* ## Validation Severity Levels
|
|
19
|
+
*
|
|
20
|
+
* - **Error**: Blocking issues that prevent domain publication
|
|
21
|
+
* - **Warning**: Issues that may cause problems but don't block publication
|
|
22
|
+
* - **Info**: Recommendations for best practices
|
|
23
|
+
*
|
|
24
|
+
* ## Usage
|
|
25
|
+
*
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const domain = new DataDomain()
|
|
28
|
+
* // ... add entities, properties, associations
|
|
29
|
+
*
|
|
30
|
+
* const validator = new DomainValidation(domain)
|
|
31
|
+
* const report = validator.validate()
|
|
32
|
+
*
|
|
33
|
+
* if (report.canProceed) {
|
|
34
|
+
* // Domain is valid and can be published
|
|
35
|
+
* } else {
|
|
36
|
+
* // Handle validation errors
|
|
37
|
+
* console.log(report.impact)
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* ## Validation Rules
|
|
42
|
+
*
|
|
43
|
+
* ### Entity Validation Rules
|
|
44
|
+
* - **Primary Key**: Each entity must have a primary key (can be inherited)
|
|
45
|
+
* - **Minimum Properties**: Entities should have properties or associations (can be inherited)
|
|
46
|
+
* - **Naming**: Entity names must follow PostgreSQL naming conventions
|
|
47
|
+
* - **Uniqueness**: Entity names must be unique within the domain
|
|
48
|
+
*
|
|
49
|
+
* ### Property Validation Rules
|
|
50
|
+
* - **Naming**: Properties must follow PostgreSQL column naming conventions
|
|
51
|
+
* - **Length**: Names must be 2-59 characters long
|
|
52
|
+
* - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
|
|
53
|
+
* - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
|
|
54
|
+
* - **Case**: Snake case is recommended (lowercase with underscores)
|
|
55
|
+
*
|
|
56
|
+
* ### Association Validation Rules
|
|
57
|
+
* - **Targets**: Associations must have at least one target entity
|
|
58
|
+
* - **Target Existence**: Target entities must exist in the domain
|
|
59
|
+
* - **Naming**: Same rules as properties
|
|
60
|
+
*
|
|
61
|
+
* ### Semantic Validation Rules
|
|
62
|
+
* - **User Entity**: Recommended to have at least one entity with User semantic
|
|
63
|
+
* - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
|
|
64
|
+
* - **Soft Delete**: Recommended to have soft delete capability for entities
|
|
65
|
+
* - **Data Types**: Semantic-specific data type validation
|
|
66
|
+
*
|
|
67
|
+
* @see {@link EntityValidation} for detailed entity validation rules
|
|
68
|
+
* @see {@link PropertyValidation} for detailed property validation rules
|
|
69
|
+
* @see {@link AssociationValidation} for detailed association validation rules
|
|
70
|
+
* @see {@link SemanticValidation} for detailed semantic validation rules
|
|
71
|
+
*/
|
|
3
72
|
export declare class DomainValidation {
|
|
4
73
|
private root;
|
|
5
74
|
constructor(root: DataDomain);
|
|
75
|
+
/**
|
|
76
|
+
* Performs comprehensive validation on the entire data domain.
|
|
77
|
+
*
|
|
78
|
+
* This method validates all entities, properties, associations, and semantics
|
|
79
|
+
* in the domain. It returns a detailed report of all validation issues found,
|
|
80
|
+
* categorized by severity level.
|
|
81
|
+
*
|
|
82
|
+
* @returns A comprehensive validation report with all issues found
|
|
83
|
+
*/
|
|
6
84
|
validate(): DomainImpactReport;
|
|
7
85
|
}
|
|
8
86
|
//# sourceMappingURL=DomainValidation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DomainValidation.d.ts","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAM/C,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAY;gBAEZ,IAAI,EAAE,UAAU;IAI5B,QAAQ,IAAI,kBAAkB;CAyF/B"}
|
|
1
|
+
{"version":3,"file":"DomainValidation.d.ts","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAM/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAY;gBAEZ,IAAI,EAAE,UAAU;IAI5B;;;;;;;;OAQG;IACH,QAAQ,IAAI,kBAAkB;CAyF/B"}
|
|
@@ -3,11 +3,89 @@ import { AssociationValidation } from './validation/association_validation.js';
|
|
|
3
3
|
import { EntityValidation } from './validation/entity_validation.js';
|
|
4
4
|
import { PropertyValidation } from './validation/property_validation.js';
|
|
5
5
|
import { SemanticValidation } from './validation/semantic_validation.js';
|
|
6
|
+
/**
|
|
7
|
+
* DomainValidation performs comprehensive validation on a data domain and its components.
|
|
8
|
+
*
|
|
9
|
+
* This class orchestrates validation across all domain elements including entities, properties,
|
|
10
|
+
* associations, and semantics. It ensures that the data domain is well-formed and follows
|
|
11
|
+
* established conventions before it can be published or used in API generation.
|
|
12
|
+
*
|
|
13
|
+
* ## Validation Scope
|
|
14
|
+
*
|
|
15
|
+
* The validation process covers:
|
|
16
|
+
* - **Entities**: Structure, naming, primary keys, and inheritance
|
|
17
|
+
* - **Properties**: Naming conventions, data types, and constraints
|
|
18
|
+
* - **Associations**: Relationships, targets, and naming
|
|
19
|
+
* - **Semantics**: Recommended patterns and best practices
|
|
20
|
+
*
|
|
21
|
+
* ## Validation Severity Levels
|
|
22
|
+
*
|
|
23
|
+
* - **Error**: Blocking issues that prevent domain publication
|
|
24
|
+
* - **Warning**: Issues that may cause problems but don't block publication
|
|
25
|
+
* - **Info**: Recommendations for best practices
|
|
26
|
+
*
|
|
27
|
+
* ## Usage
|
|
28
|
+
*
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const domain = new DataDomain()
|
|
31
|
+
* // ... add entities, properties, associations
|
|
32
|
+
*
|
|
33
|
+
* const validator = new DomainValidation(domain)
|
|
34
|
+
* const report = validator.validate()
|
|
35
|
+
*
|
|
36
|
+
* if (report.canProceed) {
|
|
37
|
+
* // Domain is valid and can be published
|
|
38
|
+
* } else {
|
|
39
|
+
* // Handle validation errors
|
|
40
|
+
* console.log(report.impact)
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* ## Validation Rules
|
|
45
|
+
*
|
|
46
|
+
* ### Entity Validation Rules
|
|
47
|
+
* - **Primary Key**: Each entity must have a primary key (can be inherited)
|
|
48
|
+
* - **Minimum Properties**: Entities should have properties or associations (can be inherited)
|
|
49
|
+
* - **Naming**: Entity names must follow PostgreSQL naming conventions
|
|
50
|
+
* - **Uniqueness**: Entity names must be unique within the domain
|
|
51
|
+
*
|
|
52
|
+
* ### Property Validation Rules
|
|
53
|
+
* - **Naming**: Properties must follow PostgreSQL column naming conventions
|
|
54
|
+
* - **Length**: Names must be 2-59 characters long
|
|
55
|
+
* - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
|
|
56
|
+
* - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
|
|
57
|
+
* - **Case**: Snake case is recommended (lowercase with underscores)
|
|
58
|
+
*
|
|
59
|
+
* ### Association Validation Rules
|
|
60
|
+
* - **Targets**: Associations must have at least one target entity
|
|
61
|
+
* - **Target Existence**: Target entities must exist in the domain
|
|
62
|
+
* - **Naming**: Same rules as properties
|
|
63
|
+
*
|
|
64
|
+
* ### Semantic Validation Rules
|
|
65
|
+
* - **User Entity**: Recommended to have at least one entity with User semantic
|
|
66
|
+
* - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
|
|
67
|
+
* - **Soft Delete**: Recommended to have soft delete capability for entities
|
|
68
|
+
* - **Data Types**: Semantic-specific data type validation
|
|
69
|
+
*
|
|
70
|
+
* @see {@link EntityValidation} for detailed entity validation rules
|
|
71
|
+
* @see {@link PropertyValidation} for detailed property validation rules
|
|
72
|
+
* @see {@link AssociationValidation} for detailed association validation rules
|
|
73
|
+
* @see {@link SemanticValidation} for detailed semantic validation rules
|
|
74
|
+
*/
|
|
6
75
|
export class DomainValidation {
|
|
7
76
|
root;
|
|
8
77
|
constructor(root) {
|
|
9
78
|
this.root = root;
|
|
10
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Performs comprehensive validation on the entire data domain.
|
|
82
|
+
*
|
|
83
|
+
* This method validates all entities, properties, associations, and semantics
|
|
84
|
+
* in the domain. It returns a detailed report of all validation issues found,
|
|
85
|
+
* categorized by severity level.
|
|
86
|
+
*
|
|
87
|
+
* @returns A comprehensive validation report with all issues found
|
|
88
|
+
*/
|
|
11
89
|
validate() {
|
|
12
90
|
const result = {
|
|
13
91
|
key: '',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DomainValidation.js","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"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","sourcesContent":["import { DataDomainKind } from '../models/kinds.js'\nimport { DataDomain } from './DataDomain.js'\nimport { DomainImpactReport } from './types.js'\nimport { AssociationValidation } from './validation/association_validation.js'\nimport { EntityValidation } from './validation/entity_validation.js'\nimport { PropertyValidation } from './validation/property_validation.js'\nimport { SemanticValidation } from './validation/semantic_validation.js'\n\nexport class DomainValidation {\n private root: DataDomain\n\n constructor(root: DataDomain) {\n this.root = root\n }\n\n validate(): DomainImpactReport {\n const result: DomainImpactReport = {\n key: '',\n kind: DataDomainKind,\n impact: [],\n canProceed: true,\n }\n const entityValidator = new EntityValidation(this.root)\n const propertyValidator = new PropertyValidation(this.root)\n const associationValidator = new AssociationValidation(this.root)\n const semanticValidator = new SemanticValidation(this.root)\n\n let hasEntities = false\n // Validate entities, properties, and associations\n for (const entity of this.root.listEntities()) {\n if (entity.domain.key !== this.root.key) {\n // we don't need to validate foreign entities\n continue\n }\n hasEntities = true\n const report = entityValidator.validate(entity)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n for (const property of entity.properties) {\n const report = propertyValidator.validate(property)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n for (const association of entity.associations) {\n const report = associationValidator.validate(association)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n }\n if (!hasEntities) {\n // no entities, no need to validate anything else\n return result\n }\n // Validate semantics\n const semanticReport = semanticValidator.validate()\n for (const item of semanticReport) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n\n return result\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DomainValidation.js","sourceRoot":"","sources":["../../../src/modeling/DomainValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAA;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,MAAM,OAAO,gBAAgB;IACnB,IAAI,CAAY;IAExB,YAAY,IAAgB;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED;;;;;;;;OAQG;IACH,QAAQ;QACN,MAAM,MAAM,GAAuB;YACjC,GAAG,EAAE,EAAE;YACP,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,EAAE;YACV,UAAU,EAAE,IAAI;SACjB,CAAA;QACD,MAAM,eAAe,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACvD,MAAM,iBAAiB,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC3D,MAAM,oBAAoB,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjE,MAAM,iBAAiB,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE3D,IAAI,WAAW,GAAG,KAAK,CAAA;QACvB,kDAAkD;QAClD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACxC,6CAA6C;gBAC7C,SAAQ;YACV,CAAC;YACD,WAAW,GAAG,IAAI,CAAA;YAClB,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAC/C,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;gBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;gBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,IAAI,CAAC,OAAO;oBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;oBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAA;YACJ,CAAC;YACD,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;gBACnD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;oBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;oBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,IAAI,CAAC,OAAO;wBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;wBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;qBACpB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;gBACzD,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;oBAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;oBAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;wBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,SAAS;wBACf,MAAM,EAAE,IAAI,CAAC,OAAO;wBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;wBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;qBACpB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,iDAAiD;YACjD,OAAO,MAAM,CAAA;QACf,CAAC;QACD,qBAAqB;QACrB,MAAM,cAAc,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAA;QACnD,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,OAAO,CAAA;YAC1C,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,QAAQ,CAAA;YAClD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBACjB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,IAAI,CAAC,OAAO;gBACpB,UAAU,EAAE,IAAI,CAAC,IAAI;gBACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CACF","sourcesContent":["import { DataDomainKind } from '../models/kinds.js'\nimport { DataDomain } from './DataDomain.js'\nimport { DomainImpactReport } from './types.js'\nimport { AssociationValidation } from './validation/association_validation.js'\nimport { EntityValidation } from './validation/entity_validation.js'\nimport { PropertyValidation } from './validation/property_validation.js'\nimport { SemanticValidation } from './validation/semantic_validation.js'\n\n/**\n * DomainValidation performs comprehensive validation on a data domain and its components.\n *\n * This class orchestrates validation across all domain elements including entities, properties,\n * associations, and semantics. It ensures that the data domain is well-formed and follows\n * established conventions before it can be published or used in API generation.\n *\n * ## Validation Scope\n *\n * The validation process covers:\n * - **Entities**: Structure, naming, primary keys, and inheritance\n * - **Properties**: Naming conventions, data types, and constraints\n * - **Associations**: Relationships, targets, and naming\n * - **Semantics**: Recommended patterns and best practices\n *\n * ## Validation Severity Levels\n *\n * - **Error**: Blocking issues that prevent domain publication\n * - **Warning**: Issues that may cause problems but don't block publication\n * - **Info**: Recommendations for best practices\n *\n * ## Usage\n *\n * ```typescript\n * const domain = new DataDomain()\n * // ... add entities, properties, associations\n *\n * const validator = new DomainValidation(domain)\n * const report = validator.validate()\n *\n * if (report.canProceed) {\n * // Domain is valid and can be published\n * } else {\n * // Handle validation errors\n * console.log(report.impact)\n * }\n * ```\n *\n * ## Validation Rules\n *\n * ### Entity Validation Rules\n * - **Primary Key**: Each entity must have a primary key (can be inherited)\n * - **Minimum Properties**: Entities should have properties or associations (can be inherited)\n * - **Naming**: Entity names must follow PostgreSQL naming conventions\n * - **Uniqueness**: Entity names must be unique within the domain\n *\n * ### Property Validation Rules\n * - **Naming**: Properties must follow PostgreSQL column naming conventions\n * - **Length**: Names must be 2-59 characters long\n * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore\n * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords\n * - **Case**: Snake case is recommended (lowercase with underscores)\n *\n * ### Association Validation Rules\n * - **Targets**: Associations must have at least one target entity\n * - **Target Existence**: Target entities must exist in the domain\n * - **Naming**: Same rules as properties\n *\n * ### Semantic Validation Rules\n * - **User Entity**: Recommended to have at least one entity with User semantic\n * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties\n * - **Soft Delete**: Recommended to have soft delete capability for entities\n * - **Data Types**: Semantic-specific data type validation\n *\n * @see {@link EntityValidation} for detailed entity validation rules\n * @see {@link PropertyValidation} for detailed property validation rules\n * @see {@link AssociationValidation} for detailed association validation rules\n * @see {@link SemanticValidation} for detailed semantic validation rules\n */\nexport class DomainValidation {\n private root: DataDomain\n\n constructor(root: DataDomain) {\n this.root = root\n }\n\n /**\n * Performs comprehensive validation on the entire data domain.\n *\n * This method validates all entities, properties, associations, and semantics\n * in the domain. It returns a detailed report of all validation issues found,\n * categorized by severity level.\n *\n * @returns A comprehensive validation report with all issues found\n */\n validate(): DomainImpactReport {\n const result: DomainImpactReport = {\n key: '',\n kind: DataDomainKind,\n impact: [],\n canProceed: true,\n }\n const entityValidator = new EntityValidation(this.root)\n const propertyValidator = new PropertyValidation(this.root)\n const associationValidator = new AssociationValidation(this.root)\n const semanticValidator = new SemanticValidation(this.root)\n\n let hasEntities = false\n // Validate entities, properties, and associations\n for (const entity of this.root.listEntities()) {\n if (entity.domain.key !== this.root.key) {\n // we don't need to validate foreign entities\n continue\n }\n hasEntities = true\n const report = entityValidator.validate(entity)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n for (const property of entity.properties) {\n const report = propertyValidator.validate(property)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n for (const association of entity.associations) {\n const report = associationValidator.validate(association)\n for (const item of report) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n }\n }\n if (!hasEntities) {\n // no entities, no need to validate anything else\n return result\n }\n // Validate semantics\n const semanticReport = semanticValidator.validate()\n for (const item of semanticReport) {\n const blocking = item.severity === 'error'\n result.canProceed = result.canProceed && !blocking\n result.impact.push({\n key: item.key,\n kind: item.kind,\n type: 'publish',\n impact: item.message,\n resolution: item.help,\n severity: item.severity,\n parent: item.parent,\n })\n }\n\n return result\n }\n}\n"]}
|
|
@@ -14,15 +14,29 @@ export declare class EntityValidation {
|
|
|
14
14
|
/**
|
|
15
15
|
* Performs all the validation rules on the entity.
|
|
16
16
|
* If you are interested in a specific rule, use the specific method.
|
|
17
|
-
* @param target The target entity to validate. Can be a string with
|
|
17
|
+
* @param target The target entity to validate. Can be a string with
|
|
18
|
+
* the entity key or a DomainEntity object.
|
|
18
19
|
*/
|
|
19
20
|
validate(target: string | DomainEntity): DomainValidation[];
|
|
20
21
|
/**
|
|
21
|
-
* Validates the entity
|
|
22
|
+
* Validates the entity primary key.
|
|
22
23
|
* @param entity The entity to validate
|
|
23
|
-
* @returns The list of validation messages.
|
|
24
24
|
*/
|
|
25
25
|
validatePrimaryKey(entity: DomainEntity): DomainValidation[];
|
|
26
|
+
/**
|
|
27
|
+
* Checks if an entity has properties through its entire inheritance chain.
|
|
28
|
+
* This includes direct properties and properties inherited from any level of parent entities.
|
|
29
|
+
* @param entity The entity to check
|
|
30
|
+
* @returns True if the entity has properties either directly or through inheritance
|
|
31
|
+
*/
|
|
32
|
+
private hasPropertiesInherited;
|
|
33
|
+
/**
|
|
34
|
+
* Checks if an entity has associations through its entire inheritance chain.
|
|
35
|
+
* This includes direct associations and associations inherited from any level of parent entities.
|
|
36
|
+
* @param entity The entity to check
|
|
37
|
+
* @returns True if the entity has associations either directly or through inheritance
|
|
38
|
+
*/
|
|
39
|
+
private hasAssociationsInherited;
|
|
26
40
|
/**
|
|
27
41
|
* Checks if the entity has the minimum required properties.
|
|
28
42
|
* @param entity The entity to validate
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity_validation.d.ts","sourceRoot":"","sources":["../../../../src/modeling/validation/entity_validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IACf,SAAS,CAAC,MAAM,EAAE,UAAU;gBAAlB,MAAM,EAAE,UAAU;IAExC
|
|
1
|
+
{"version":3,"file":"entity_validation.d.ts","sourceRoot":"","sources":["../../../../src/modeling/validation/entity_validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IACf,SAAS,CAAC,MAAM,EAAE,UAAU;gBAAlB,MAAM,EAAE,UAAU;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,gBAAgB,EAAE;IAiC3D;;;OAGG;IACH,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,EAAE;IAmB5D;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;IAgBhC;;;OAGG;IACH,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,EAAE;IAuBnE;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,EAAE;IAuFtD;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,EAAE;CAwCrD"}
|
|
@@ -15,7 +15,8 @@ export class EntityValidation {
|
|
|
15
15
|
/**
|
|
16
16
|
* Performs all the validation rules on the entity.
|
|
17
17
|
* If you are interested in a specific rule, use the specific method.
|
|
18
|
-
* @param target The target entity to validate. Can be a string with
|
|
18
|
+
* @param target The target entity to validate. Can be a string with
|
|
19
|
+
* the entity key or a DomainEntity object.
|
|
19
20
|
*/
|
|
20
21
|
validate(target) {
|
|
21
22
|
const results = [];
|
|
@@ -40,20 +41,19 @@ export class EntityValidation {
|
|
|
40
41
|
});
|
|
41
42
|
return results;
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
-
results.push(...
|
|
45
|
-
const
|
|
46
|
-
results.push(...
|
|
44
|
+
const primaryKey = this.validatePrimaryKey(entity);
|
|
45
|
+
results.push(...primaryKey);
|
|
46
|
+
const minimumProperties = this.minimumRequiredProperties(entity);
|
|
47
|
+
results.push(...minimumProperties);
|
|
47
48
|
const name = this.validateName(entity);
|
|
48
49
|
results.push(...name);
|
|
49
|
-
const
|
|
50
|
-
results.push(...
|
|
50
|
+
const unique = this.uniqueName(entity);
|
|
51
|
+
results.push(...unique);
|
|
51
52
|
return results;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
|
-
* Validates the entity
|
|
55
|
+
* Validates the entity primary key.
|
|
55
56
|
* @param entity The entity to validate
|
|
56
|
-
* @returns The list of validation messages.
|
|
57
57
|
*/
|
|
58
58
|
validatePrimaryKey(entity) {
|
|
59
59
|
const results = [];
|
|
@@ -73,13 +73,54 @@ export class EntityValidation {
|
|
|
73
73
|
}
|
|
74
74
|
return results;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks if an entity has properties through its entire inheritance chain.
|
|
78
|
+
* This includes direct properties and properties inherited from any level of parent entities.
|
|
79
|
+
* @param entity The entity to check
|
|
80
|
+
* @returns True if the entity has properties either directly or through inheritance
|
|
81
|
+
*/
|
|
82
|
+
hasPropertiesInherited(entity) {
|
|
83
|
+
// Check direct properties first
|
|
84
|
+
if (entity.hasProperties()) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
// Check all parents recursively
|
|
88
|
+
for (const parent of entity.listParents()) {
|
|
89
|
+
if (this.hasPropertiesInherited(parent)) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Checks if an entity has associations through its entire inheritance chain.
|
|
97
|
+
* This includes direct associations and associations inherited from any level of parent entities.
|
|
98
|
+
* @param entity The entity to check
|
|
99
|
+
* @returns True if the entity has associations either directly or through inheritance
|
|
100
|
+
*/
|
|
101
|
+
hasAssociationsInherited(entity) {
|
|
102
|
+
// Check direct associations first
|
|
103
|
+
if (entity.hasAssociations()) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
// Check all parents recursively
|
|
107
|
+
for (const parent of entity.listParents()) {
|
|
108
|
+
if (this.hasAssociationsInherited(parent)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
76
114
|
/**
|
|
77
115
|
* Checks if the entity has the minimum required properties.
|
|
78
116
|
* @param entity The entity to validate
|
|
79
117
|
*/
|
|
80
118
|
minimumRequiredProperties(entity) {
|
|
81
119
|
const results = [];
|
|
82
|
-
if
|
|
120
|
+
// Check if entity has properties or associations through entire inheritance chain
|
|
121
|
+
const hasProperties = this.hasPropertiesInherited(entity);
|
|
122
|
+
const hasAssociations = this.hasAssociationsInherited(entity);
|
|
123
|
+
if (!hasProperties && !hasAssociations) {
|
|
83
124
|
const message = `The "${entity.info.getLabel()}" entity has no properties. It will be ignored.`;
|
|
84
125
|
const help = `Entities that have no properties are ignored in the data domain. No schema will be generated for it.`;
|
|
85
126
|
results.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity_validation.js","sourceRoot":"","sources":["../../../../src/modeling/validation/entity_validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { DomainEntityKind } from '../../models/kinds.js'\nimport type { DataDomain } from '../DataDomain.js'\nimport type { DomainEntity } from '../DomainEntity.js'\nimport { ReservedKeywords } from './postgresql.js'\nimport type { DomainValidation } from './rules.js'\n\n/**\n * EntityValidation is a class that performs validation on entities in a data domain.\n *\n * @remarks\n * - We do not need to check for parent uniqueness here, because in the graph there can only be one edge\n * between two nodes. Parent relationships are described by an edge in the graph.\n */\nexport class EntityValidation {\n constructor(protected domain: DataDomain) {}\n\n /**\n * Performs all the validation rules on the entity.\n * If you are interested in a specific rule, use the specific method.\n * @param target The target entity to validate. Can be a string with the entity key or a DomainEntity object.\n */\n validate(target: string | DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n let entity: DomainEntity | undefined\n if (typeof target === 'string') {\n entity = this.domain.findEntity(target)\n } else {\n entity = target\n }\n if (!entity) {\n const message = `The \"${target}\" entity does not exist.`\n const help = `The entity must be defined in the domain.`\n results.push({\n field: '*',\n rule: 'exists',\n message,\n help,\n key: target as string,\n kind: DomainEntityKind,\n severity: 'error',\n })\n return results\n }\n const primary = this.validatePrimaryKey(entity)\n results.push(...primary)\n const minimum = this.minimumRequiredProperties(entity)\n results.push(...minimum)\n const name = this.validateName(entity)\n results.push(...name)\n const uniqueName = this.uniqueName(entity)\n results.push(...uniqueName)\n return results\n }\n\n /**\n * Validates the entity against the primary key validation rules.\n * @param entity The entity to validate\n * @returns The list of validation messages.\n */\n validatePrimaryKey(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const primary = entity.primaryKey()\n if (!primary) {\n const message = `The \"${entity.info.getLabel()}\" entity has no identifier.`\n const help = `An entity that can exists by itself must have identifier defined.`\n results.push({\n field: 'properties',\n rule: 'primary_key',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Checks if the entity has the minimum required properties.\n * @param entity The entity to validate\n */\n minimumRequiredProperties(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n if (!entity.hasProperties() && !entity.hasAssociations()) {\n const message = `The \"${entity.info.getLabel()}\" entity has no properties. It will be ignored.`\n const help = `Entities that have no properties are ignored in the data domain. No schema will be generated for it.`\n results.push({\n field: 'properties',\n rule: 'required',\n message,\n help,\n severity: 'warning',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Validates the entity name.\n *\n * @remarks\n * - An entity must have a name defined.\n * - The name has to follow the same rules as the names in a PostgreSQL database.\n * - Table names must start with a letter (a-z) or underscore (_).\n * - Subsequent characters can be letters, digits (0-9), or underscores (_).\n * - Names are case-insensitive.\n * - The maximum length of a table name is 31 characters\n * - Should be snake case (it's a convention, not an error).\n * - Should not be a reserved word (for example: \"IN\", \"SELECT\", \"FROM\", etc.).\n * @param entity The entity to validate\n */\n validateName(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const label = entity.info.getLabel()\n if (!entity.info.name) {\n const message = `The \"${label}\" entity has no name.`\n const help = `An entity must have a name.`\n results.push({\n field: 'name',\n rule: 'required',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n return results\n }\n\n const name = entity.info.name\n if (name.length < 2) {\n const message = `The \"${label}\" entity name is too short.`\n const help = `The name must be at least 2 characters long.`\n results.push({\n field: 'name',\n rule: 'length',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (name.length > 31) {\n const message = `The \"${label}\" entity name is too long.`\n const help = `The name must be at most 31 characters long.`\n results.push({\n field: 'name',\n rule: 'length',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n const message = `The \"${label}\" entity name is invalid.`\n const help = `The name must start with a letter (a-z) or underscore (_). Subsequent characters can be letters, digits (0-9), or underscores (_).`\n results.push({\n field: 'name',\n rule: 'format',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (/^[A-Z]/.test(name)) {\n const message = `The \"${label}\" entity name is not in snake case.`\n const help = `The name should be in snake case (lowercase letters and underscores).`\n results.push({\n field: 'name',\n rule: 'snake_case',\n message,\n help,\n severity: 'info',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (ReservedKeywords.has(name.toUpperCase())) {\n const message = `The \"${label}\" entity name is a reserved keyword.`\n const help = `The name should not be a reserved keyword.`\n results.push({\n field: 'name',\n rule: 'reserved',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Checks if the entity name is unique in the data domain.\n * @param entity The entity to validate\n */\n uniqueName(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const name = entity.info.name?.toLowerCase()\n if (!name) {\n return results\n }\n // We need to check the unique names in all entities, including foreign ones.\n for (const other of this.domain.listEntities()) {\n if (other.info.name?.toLowerCase() === name && other.key !== entity.key) {\n const message = `The \"${name}\" entity name is already used in the data domain.`\n const help = `The name must be unique. This includes references to other data domains.`\n results.push({\n field: 'name',\n rule: 'unique',\n message,\n help,\n severity: 'error',\n key: other.key,\n kind: other.kind,\n })\n }\n }\n for (const other of this.domain.listAllForeignEntities()) {\n if (other.info.name?.toLowerCase() === name && other.key !== entity.key) {\n const message = `The \"${name}\" entity name is already used in the foreign data domain.`\n const help = `The name must be unique. This includes references to other data domains.`\n results.push({\n field: 'name',\n rule: 'unique',\n message,\n help,\n severity: 'error',\n key: other.key,\n kind: other.kind,\n parent: other.domain.key,\n })\n }\n }\n return results\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entity_validation.js","sourceRoot":"","sources":["../../../../src/modeling/validation/entity_validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { DomainEntityKind } from '../../models/kinds.js'\nimport type { DataDomain } from '../DataDomain.js'\nimport type { DomainEntity } from '../DomainEntity.js'\nimport { ReservedKeywords } from './postgresql.js'\nimport type { DomainValidation } from './rules.js'\n\n/**\n * EntityValidation is a class that performs validation on entities in a data domain.\n *\n * @remarks\n * - We do not need to check for parent uniqueness here, because in the graph there can only be one edge\n * between two nodes. Parent relationships are described by an edge in the graph.\n */\nexport class EntityValidation {\n constructor(protected domain: DataDomain) {}\n\n /**\n * Performs all the validation rules on the entity.\n * If you are interested in a specific rule, use the specific method.\n * @param target The target entity to validate. Can be a string with\n * the entity key or a DomainEntity object.\n */\n validate(target: string | DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n let entity: DomainEntity | undefined\n if (typeof target === 'string') {\n entity = this.domain.findEntity(target)\n } else {\n entity = target\n }\n if (!entity) {\n const message = `The \"${target}\" entity does not exist.`\n const help = `The entity must be defined in the domain.`\n results.push({\n field: '*',\n rule: 'exists',\n message,\n help,\n key: target as string,\n kind: DomainEntityKind,\n severity: 'error',\n })\n return results\n }\n const primaryKey = this.validatePrimaryKey(entity)\n results.push(...primaryKey)\n const minimumProperties = this.minimumRequiredProperties(entity)\n results.push(...minimumProperties)\n const name = this.validateName(entity)\n results.push(...name)\n const unique = this.uniqueName(entity)\n results.push(...unique)\n return results\n }\n\n /**\n * Validates the entity primary key.\n * @param entity The entity to validate\n */\n validatePrimaryKey(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const primary = entity.primaryKey()\n if (!primary) {\n const message = `The \"${entity.info.getLabel()}\" entity has no identifier.`\n const help = `An entity that can exists by itself must have identifier defined.`\n results.push({\n field: 'properties',\n rule: 'primary_key',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Checks if an entity has properties through its entire inheritance chain.\n * This includes direct properties and properties inherited from any level of parent entities.\n * @param entity The entity to check\n * @returns True if the entity has properties either directly or through inheritance\n */\n private hasPropertiesInherited(entity: DomainEntity): boolean {\n // Check direct properties first\n if (entity.hasProperties()) {\n return true\n }\n\n // Check all parents recursively\n for (const parent of entity.listParents()) {\n if (this.hasPropertiesInherited(parent)) {\n return true\n }\n }\n\n return false\n }\n\n /**\n * Checks if an entity has associations through its entire inheritance chain.\n * This includes direct associations and associations inherited from any level of parent entities.\n * @param entity The entity to check\n * @returns True if the entity has associations either directly or through inheritance\n */\n private hasAssociationsInherited(entity: DomainEntity): boolean {\n // Check direct associations first\n if (entity.hasAssociations()) {\n return true\n }\n\n // Check all parents recursively\n for (const parent of entity.listParents()) {\n if (this.hasAssociationsInherited(parent)) {\n return true\n }\n }\n\n return false\n }\n\n /**\n * Checks if the entity has the minimum required properties.\n * @param entity The entity to validate\n */\n minimumRequiredProperties(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n\n // Check if entity has properties or associations through entire inheritance chain\n const hasProperties = this.hasPropertiesInherited(entity)\n const hasAssociations = this.hasAssociationsInherited(entity)\n\n if (!hasProperties && !hasAssociations) {\n const message = `The \"${entity.info.getLabel()}\" entity has no properties. It will be ignored.`\n const help = `Entities that have no properties are ignored in the data domain. No schema will be generated for it.`\n results.push({\n field: 'properties',\n rule: 'required',\n message,\n help,\n severity: 'warning',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Validates the entity name.\n *\n * @remarks\n * - An entity must have a name defined.\n * - The name has to follow the same rules as the names in a PostgreSQL database.\n * - Table names must start with a letter (a-z) or underscore (_).\n * - Subsequent characters can be letters, digits (0-9), or underscores (_).\n * - Names are case-insensitive.\n * - The maximum length of a table name is 31 characters\n * - Should be snake case (it's a convention, not an error).\n * - Should not be a reserved word (for example: \"IN\", \"SELECT\", \"FROM\", etc.).\n * @param entity The entity to validate\n */\n validateName(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const label = entity.info.getLabel()\n if (!entity.info.name) {\n const message = `The \"${label}\" entity has no name.`\n const help = `An entity must have a name.`\n results.push({\n field: 'name',\n rule: 'required',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n return results\n }\n\n const name = entity.info.name\n if (name.length < 2) {\n const message = `The \"${label}\" entity name is too short.`\n const help = `The name must be at least 2 characters long.`\n results.push({\n field: 'name',\n rule: 'length',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (name.length > 31) {\n const message = `The \"${label}\" entity name is too long.`\n const help = `The name must be at most 31 characters long.`\n results.push({\n field: 'name',\n rule: 'length',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n const message = `The \"${label}\" entity name is invalid.`\n const help = `The name must start with a letter (a-z) or underscore (_). Subsequent characters can be letters, digits (0-9), or underscores (_).`\n results.push({\n field: 'name',\n rule: 'format',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (/^[A-Z]/.test(name)) {\n const message = `The \"${label}\" entity name is not in snake case.`\n const help = `The name should be in snake case (lowercase letters and underscores).`\n results.push({\n field: 'name',\n rule: 'snake_case',\n message,\n help,\n severity: 'info',\n key: entity.key,\n kind: entity.kind,\n })\n }\n if (ReservedKeywords.has(name.toUpperCase())) {\n const message = `The \"${label}\" entity name is a reserved keyword.`\n const help = `The name should not be a reserved keyword.`\n results.push({\n field: 'name',\n rule: 'reserved',\n message,\n help,\n severity: 'error',\n key: entity.key,\n kind: entity.kind,\n })\n }\n return results\n }\n\n /**\n * Checks if the entity name is unique in the data domain.\n * @param entity The entity to validate\n */\n uniqueName(entity: DomainEntity): DomainValidation[] {\n const results: DomainValidation[] = []\n const name = entity.info.name?.toLowerCase()\n if (!name) {\n return results\n }\n // We need to check the unique names in all entities, including foreign ones.\n for (const other of this.domain.listEntities()) {\n if (other.info.name?.toLowerCase() === name && other.key !== entity.key) {\n const message = `The \"${name}\" entity name is already used in the data domain.`\n const help = `The name must be unique. This includes references to other data domains.`\n results.push({\n field: 'name',\n rule: 'unique',\n message,\n help,\n severity: 'error',\n key: other.key,\n kind: other.kind,\n })\n }\n }\n for (const other of this.domain.listAllForeignEntities()) {\n if (other.info.name?.toLowerCase() === name && other.key !== entity.key) {\n const message = `The \"${name}\" entity name is already used in the foreign data domain.`\n const help = `The name must be unique. This includes references to other data domains.`\n results.push({\n field: 'name',\n rule: 'unique',\n message,\n help,\n severity: 'error',\n key: other.key,\n kind: other.kind,\n parent: other.domain.key,\n })\n }\n }\n return results\n }\n}\n"]}
|