@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.
@@ -42065,10 +42065,10 @@
42065
42065
  "@id": "#191"
42066
42066
  },
42067
42067
  {
42068
- "@id": "#197"
42068
+ "@id": "#194"
42069
42069
  },
42070
42070
  {
42071
- "@id": "#194"
42071
+ "@id": "#197"
42072
42072
  },
42073
42073
  {
42074
42074
  "@id": "#200"
@@ -43457,7 +43457,7 @@
43457
43457
  "doc:ExternalDomainElement",
43458
43458
  "doc:DomainElement"
43459
43459
  ],
43460
- "doc:raw": "code: '5'\ndescription: 'Limited company'\n",
43460
+ "doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
43461
43461
  "core:mediaType": "application/yaml",
43462
43462
  "sourcemaps:sources": [
43463
43463
  {
@@ -43478,7 +43478,7 @@
43478
43478
  "doc:ExternalDomainElement",
43479
43479
  "doc:DomainElement"
43480
43480
  ],
43481
- "doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
43481
+ "doc:raw": "code: '5'\ndescription: 'Limited company'\n",
43482
43482
  "core:mediaType": "application/yaml",
43483
43483
  "sourcemaps:sources": [
43484
43484
  {
@@ -44761,12 +44761,12 @@
44761
44761
  {
44762
44762
  "@id": "#196/source-map/lexical/element_0",
44763
44763
  "sourcemaps:element": "amf://id#196",
44764
- "sourcemaps:value": "[(1,0)-(3,0)]"
44764
+ "sourcemaps:value": "[(1,0)-(10,0)]"
44765
44765
  },
44766
44766
  {
44767
44767
  "@id": "#199/source-map/lexical/element_0",
44768
44768
  "sourcemaps:element": "amf://id#199",
44769
- "sourcemaps:value": "[(1,0)-(10,0)]"
44769
+ "sourcemaps:value": "[(1,0)-(3,0)]"
44770
44770
  },
44771
44771
  {
44772
44772
  "@id": "#202/source-map/lexical/element_0",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@api-client/core",
3
3
  "description": "The API Client's core client library. Works in NodeJS and in a ES enabled browser.",
4
- "version": "0.18.13",
4
+ "version": "0.18.14",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
7
7
  "./browser.js": {
@@ -6,6 +6,75 @@ import { EntityValidation } from './validation/entity_validation.js'
6
6
  import { PropertyValidation } from './validation/property_validation.js'
7
7
  import { SemanticValidation } from './validation/semantic_validation.js'
8
8
 
9
+ /**
10
+ * DomainValidation performs comprehensive validation on a data domain and its components.
11
+ *
12
+ * This class orchestrates validation across all domain elements including entities, properties,
13
+ * associations, and semantics. It ensures that the data domain is well-formed and follows
14
+ * established conventions before it can be published or used in API generation.
15
+ *
16
+ * ## Validation Scope
17
+ *
18
+ * The validation process covers:
19
+ * - **Entities**: Structure, naming, primary keys, and inheritance
20
+ * - **Properties**: Naming conventions, data types, and constraints
21
+ * - **Associations**: Relationships, targets, and naming
22
+ * - **Semantics**: Recommended patterns and best practices
23
+ *
24
+ * ## Validation Severity Levels
25
+ *
26
+ * - **Error**: Blocking issues that prevent domain publication
27
+ * - **Warning**: Issues that may cause problems but don't block publication
28
+ * - **Info**: Recommendations for best practices
29
+ *
30
+ * ## Usage
31
+ *
32
+ * ```typescript
33
+ * const domain = new DataDomain()
34
+ * // ... add entities, properties, associations
35
+ *
36
+ * const validator = new DomainValidation(domain)
37
+ * const report = validator.validate()
38
+ *
39
+ * if (report.canProceed) {
40
+ * // Domain is valid and can be published
41
+ * } else {
42
+ * // Handle validation errors
43
+ * console.log(report.impact)
44
+ * }
45
+ * ```
46
+ *
47
+ * ## Validation Rules
48
+ *
49
+ * ### Entity Validation Rules
50
+ * - **Primary Key**: Each entity must have a primary key (can be inherited)
51
+ * - **Minimum Properties**: Entities should have properties or associations (can be inherited)
52
+ * - **Naming**: Entity names must follow PostgreSQL naming conventions
53
+ * - **Uniqueness**: Entity names must be unique within the domain
54
+ *
55
+ * ### Property Validation Rules
56
+ * - **Naming**: Properties must follow PostgreSQL column naming conventions
57
+ * - **Length**: Names must be 2-59 characters long
58
+ * - **Format**: Names must start with letter/underscore, contain only alphanumeric/underscore
59
+ * - **Reserved Words**: Names cannot be PostgreSQL reserved keywords
60
+ * - **Case**: Snake case is recommended (lowercase with underscores)
61
+ *
62
+ * ### Association Validation Rules
63
+ * - **Targets**: Associations must have at least one target entity
64
+ * - **Target Existence**: Target entities must exist in the domain
65
+ * - **Naming**: Same rules as properties
66
+ *
67
+ * ### Semantic Validation Rules
68
+ * - **User Entity**: Recommended to have at least one entity with User semantic
69
+ * - **Timestamps**: Recommended to have CreatedTimestamp and UpdatedTimestamp properties
70
+ * - **Soft Delete**: Recommended to have soft delete capability for entities
71
+ * - **Data Types**: Semantic-specific data type validation
72
+ *
73
+ * @see {@link EntityValidation} for detailed entity validation rules
74
+ * @see {@link PropertyValidation} for detailed property validation rules
75
+ * @see {@link AssociationValidation} for detailed association validation rules
76
+ * @see {@link SemanticValidation} for detailed semantic validation rules
77
+ */
9
78
  export class DomainValidation {
10
79
  private root: DataDomain
11
80
 
@@ -13,6 +82,15 @@ export class DomainValidation {
13
82
  this.root = root
14
83
  }
15
84
 
85
+ /**
86
+ * Performs comprehensive validation on the entire data domain.
87
+ *
88
+ * This method validates all entities, properties, associations, and semantics
89
+ * in the domain. It returns a detailed report of all validation issues found,
90
+ * categorized by severity level.
91
+ *
92
+ * @returns A comprehensive validation report with all issues found
93
+ */
16
94
  validate(): DomainImpactReport {
17
95
  const result: DomainImpactReport = {
18
96
  key: '',
@@ -17,7 +17,8 @@ export class EntityValidation {
17
17
  /**
18
18
  * Performs all the validation rules on the entity.
19
19
  * If you are interested in a specific rule, use the specific method.
20
- * @param target The target entity to validate. Can be a string with the entity key or a DomainEntity object.
20
+ * @param target The target entity to validate. Can be a string with
21
+ * the entity key or a DomainEntity object.
21
22
  */
22
23
  validate(target: string | DomainEntity): DomainValidation[] {
23
24
  const results: DomainValidation[] = []
@@ -41,21 +42,20 @@ export class EntityValidation {
41
42
  })
42
43
  return results
43
44
  }
44
- const primary = this.validatePrimaryKey(entity)
45
- results.push(...primary)
46
- const minimum = this.minimumRequiredProperties(entity)
47
- results.push(...minimum)
45
+ const primaryKey = this.validatePrimaryKey(entity)
46
+ results.push(...primaryKey)
47
+ const minimumProperties = this.minimumRequiredProperties(entity)
48
+ results.push(...minimumProperties)
48
49
  const name = this.validateName(entity)
49
50
  results.push(...name)
50
- const uniqueName = this.uniqueName(entity)
51
- results.push(...uniqueName)
51
+ const unique = this.uniqueName(entity)
52
+ results.push(...unique)
52
53
  return results
53
54
  }
54
55
 
55
56
  /**
56
- * Validates the entity against the primary key validation rules.
57
+ * Validates the entity primary key.
57
58
  * @param entity The entity to validate
58
- * @returns The list of validation messages.
59
59
  */
60
60
  validatePrimaryKey(entity: DomainEntity): DomainValidation[] {
61
61
  const results: DomainValidation[] = []
@@ -76,13 +76,62 @@ export class EntityValidation {
76
76
  return results
77
77
  }
78
78
 
79
+ /**
80
+ * Checks if an entity has properties through its entire inheritance chain.
81
+ * This includes direct properties and properties inherited from any level of parent entities.
82
+ * @param entity The entity to check
83
+ * @returns True if the entity has properties either directly or through inheritance
84
+ */
85
+ private hasPropertiesInherited(entity: DomainEntity): boolean {
86
+ // Check direct properties first
87
+ if (entity.hasProperties()) {
88
+ return true
89
+ }
90
+
91
+ // Check all parents recursively
92
+ for (const parent of entity.listParents()) {
93
+ if (this.hasPropertiesInherited(parent)) {
94
+ return true
95
+ }
96
+ }
97
+
98
+ return false
99
+ }
100
+
101
+ /**
102
+ * Checks if an entity has associations through its entire inheritance chain.
103
+ * This includes direct associations and associations inherited from any level of parent entities.
104
+ * @param entity The entity to check
105
+ * @returns True if the entity has associations either directly or through inheritance
106
+ */
107
+ private hasAssociationsInherited(entity: DomainEntity): boolean {
108
+ // Check direct associations first
109
+ if (entity.hasAssociations()) {
110
+ return true
111
+ }
112
+
113
+ // Check all parents recursively
114
+ for (const parent of entity.listParents()) {
115
+ if (this.hasAssociationsInherited(parent)) {
116
+ return true
117
+ }
118
+ }
119
+
120
+ return false
121
+ }
122
+
79
123
  /**
80
124
  * Checks if the entity has the minimum required properties.
81
125
  * @param entity The entity to validate
82
126
  */
83
127
  minimumRequiredProperties(entity: DomainEntity): DomainValidation[] {
84
128
  const results: DomainValidation[] = []
85
- if (!entity.hasProperties() && !entity.hasAssociations()) {
129
+
130
+ // Check if entity has properties or associations through entire inheritance chain
131
+ const hasProperties = this.hasPropertiesInherited(entity)
132
+ const hasAssociations = this.hasAssociationsInherited(entity)
133
+
134
+ if (!hasProperties && !hasAssociations) {
86
135
  const message = `The "${entity.info.getLabel()}" entity has no properties. It will be ignored.`
87
136
  const help = `Entities that have no properties are ignored in the data domain. No schema will be generated for it.`
88
137
  results.push({
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-len */
1
2
  import { test } from '@japa/runner'
2
3
  import { DataDomain, EntityValidation } from '../../../../src/index.js'
3
4
 
@@ -78,6 +79,100 @@ test.group('EntityValidation', (group) => {
78
79
  assert.lengthOf(results, 0)
79
80
  })
80
81
 
82
+ test('minimumRequiredProperties() should return no errors when the entity inherits properties from parent', ({
83
+ assert,
84
+ }) => {
85
+ const model = domain.addModel({ key: 'model' })
86
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
87
+ parent.addProperty({ key: 'name', type: 'string' })
88
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
89
+ entity.addParent(parent.key)
90
+ const results = validation.minimumRequiredProperties(entity)
91
+ assert.lengthOf(results, 0)
92
+ })
93
+
94
+ test('minimumRequiredProperties() should return no errors when the entity inherits associations from parent', ({
95
+ assert,
96
+ }) => {
97
+ const model = domain.addModel({ key: 'model' })
98
+ const target = model.addEntity({ key: 'target', info: { name: 'Target' } })
99
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
100
+ parent.addAssociation({ key: target.key })
101
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
102
+ entity.addParent(parent.key)
103
+ const results = validation.minimumRequiredProperties(entity)
104
+ assert.lengthOf(results, 0)
105
+ })
106
+
107
+ test('minimumRequiredProperties() should return no errors when the entity inherits properties from grandparent', ({
108
+ assert,
109
+ }) => {
110
+ const model = domain.addModel({ key: 'model' })
111
+ const grandparent = model.addEntity({
112
+ key: 'grandparent',
113
+ info: { name: 'Grandparent' },
114
+ })
115
+ grandparent.addProperty({ key: 'name', type: 'string' })
116
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
117
+ parent.addParent(grandparent.key)
118
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
119
+ entity.addParent(parent.key)
120
+ const results = validation.minimumRequiredProperties(entity)
121
+ assert.lengthOf(results, 0)
122
+ })
123
+
124
+ test('minimumRequiredProperties() should return no errors when the entity inherits associations from grandparent', ({
125
+ assert,
126
+ }) => {
127
+ const model = domain.addModel({ key: 'model' })
128
+ const target = model.addEntity({ key: 'target', info: { name: 'Target' } })
129
+ const grandparent = model.addEntity({
130
+ key: 'grandparent',
131
+ info: { name: 'Grandparent' },
132
+ })
133
+ grandparent.addAssociation({ key: target.key })
134
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
135
+ parent.addParent(grandparent.key)
136
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
137
+ entity.addParent(parent.key)
138
+ const results = validation.minimumRequiredProperties(entity)
139
+ assert.lengthOf(results, 0)
140
+ })
141
+
142
+ test('minimumRequiredProperties() should return no errors when the entity inherits properties from great-grandparent', ({
143
+ assert,
144
+ }) => {
145
+ const model = domain.addModel({ key: 'model' })
146
+ const greatGrandparent = model.addEntity({
147
+ key: 'great_grandparent',
148
+ info: { name: 'GreatGrandparent' },
149
+ })
150
+ greatGrandparent.addProperty({ key: 'name', type: 'string' })
151
+ const grandparent = model.addEntity({ key: 'grandparent', info: { name: 'Grandparent' } })
152
+ grandparent.addParent(greatGrandparent.key)
153
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
154
+ parent.addParent(grandparent.key)
155
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
156
+ entity.addParent(parent.key)
157
+ const results = validation.minimumRequiredProperties(entity)
158
+ assert.lengthOf(results, 0)
159
+ })
160
+
161
+ test('minimumRequiredProperties() should return warning when the entity has no properties in entire inheritance chain', ({
162
+ assert,
163
+ }) => {
164
+ const model = domain.addModel({ key: 'model' })
165
+ const grandparent = model.addEntity({ key: 'grandparent', info: { name: 'Grandparent' } })
166
+ const parent = model.addEntity({ key: 'parent', info: { name: 'Parent' } })
167
+ parent.addParent(grandparent.key)
168
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
169
+ entity.addParent(parent.key)
170
+ const results = validation.minimumRequiredProperties(entity)
171
+ assert.lengthOf(results, 1)
172
+ assert.equal(results[0].severity, 'warning')
173
+ assert.equal(results[0].rule, 'required')
174
+ })
175
+
81
176
  test('validateName() should return an error when the entity has no name', ({ assert }) => {
82
177
  const model = domain.addModel({ key: 'model' })
83
178
  const entity = model.addEntity({ key: 'entity', info: {} })