@api-client/core 0.18.13 → 0.18.15

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.
Files changed (25) hide show
  1. package/build/src/modeling/DomainValidation.d.ts +78 -0
  2. package/build/src/modeling/DomainValidation.d.ts.map +1 -1
  3. package/build/src/modeling/DomainValidation.js +78 -0
  4. package/build/src/modeling/DomainValidation.js.map +1 -1
  5. package/build/src/modeling/importers/FilteringJsonSchemaImporter.d.ts +84 -0
  6. package/build/src/modeling/importers/FilteringJsonSchemaImporter.d.ts.map +1 -0
  7. package/build/src/modeling/importers/FilteringJsonSchemaImporter.js +254 -0
  8. package/build/src/modeling/importers/FilteringJsonSchemaImporter.js.map +1 -0
  9. package/build/src/modeling/importers/SchemaFilteringStrategy.d.ts +72 -0
  10. package/build/src/modeling/importers/SchemaFilteringStrategy.d.ts.map +1 -0
  11. package/build/src/modeling/importers/SchemaFilteringStrategy.js +140 -0
  12. package/build/src/modeling/importers/SchemaFilteringStrategy.js.map +1 -0
  13. package/build/src/modeling/validation/entity_validation.d.ts +17 -3
  14. package/build/src/modeling/validation/entity_validation.d.ts.map +1 -1
  15. package/build/src/modeling/validation/entity_validation.js +51 -10
  16. package/build/src/modeling/validation/entity_validation.js.map +1 -1
  17. package/build/tsconfig.tsbuildinfo +1 -1
  18. package/data/models/example-generator-api.json +15 -15
  19. package/package.json +1 -1
  20. package/src/modeling/DomainValidation.ts +78 -0
  21. package/src/modeling/importers/FilteringJsonSchemaImporter.ts +324 -0
  22. package/src/modeling/importers/SchemaFilteringStrategy.ts +201 -0
  23. package/src/modeling/validation/entity_validation.ts +59 -10
  24. package/tests/unit/modeling/importers/schema_filtering.spec.ts +764 -0
  25. package/tests/unit/modeling/validation/entity_validation.spec.ts +95 -0
@@ -0,0 +1,201 @@
1
+ import type { JSONSchema7 } from 'json-schema'
2
+
3
+ /**
4
+ * Configuration options for schema filtering during import.
5
+ */
6
+ export interface SchemaFilteringOptions {
7
+ /**
8
+ * Whether to exclude schemas that are empty objects with no properties.
9
+ * Default: false
10
+ */
11
+ excludeEmptyObjects?: boolean
12
+
13
+ /**
14
+ * Whether to exclude schemas that only serve as conceptual markers.
15
+ * These are typically schemas with only type: "object" and description
16
+ * but no properties, constraints, or meaningful structure.
17
+ * Default: false
18
+ */
19
+ excludeConceptualMarkers?: boolean
20
+
21
+ /**
22
+ * Custom predicate function to determine if a schema should be excluded.
23
+ * Return true to exclude the schema from import.
24
+ */
25
+ customExclusionFilter?: (schema: JSONSchema7, schemaId?: string) => boolean
26
+
27
+ /**
28
+ * List of schema IDs to explicitly exclude.
29
+ */
30
+ excludeSchemaIds?: string[]
31
+
32
+ /**
33
+ * Patterns to match against schema titles or IDs for exclusion.
34
+ */
35
+ excludePatterns?: RegExp[]
36
+ }
37
+
38
+ /**
39
+ * Strategy class for determining which schemas should be filtered out during import.
40
+ */
41
+ export class SchemaFilteringStrategy {
42
+ constructor(private options: SchemaFilteringOptions = {}) {}
43
+
44
+ /**
45
+ * Determines whether a schema should be excluded from import.
46
+ */
47
+ shouldExcludeSchema(schema: JSONSchema7, schemaId?: string): boolean {
48
+ // Check explicit exclusion list
49
+ if (schemaId && this.options.excludeSchemaIds?.includes(schemaId)) {
50
+ return true
51
+ }
52
+
53
+ // Check pattern exclusions
54
+ if (schemaId && this.options.excludePatterns) {
55
+ for (const pattern of this.options.excludePatterns) {
56
+ if (pattern.test(schemaId) || (schema.title && pattern.test(schema.title))) {
57
+ return true
58
+ }
59
+ }
60
+ }
61
+
62
+ // Check for empty objects
63
+ if (this.options.excludeEmptyObjects && this.isEmptyObjectSchema(schema)) {
64
+ return true
65
+ }
66
+
67
+ // Check for conceptual markers
68
+ if (this.options.excludeConceptualMarkers && this.isConceptualMarkerSchema(schema)) {
69
+ return true
70
+ }
71
+
72
+ // Apply custom filter
73
+ if (this.options.customExclusionFilter?.(schema, schemaId)) {
74
+ return true
75
+ }
76
+
77
+ return false
78
+ }
79
+
80
+ /**
81
+ * Checks if a schema represents an empty object with no meaningful structure.
82
+ */
83
+ private isEmptyObjectSchema(schema: JSONSchema7): boolean {
84
+ return (
85
+ schema.type === 'object' &&
86
+ (!schema.properties || Object.keys(schema.properties).length === 0) &&
87
+ !schema.additionalProperties &&
88
+ !schema.patternProperties &&
89
+ !schema.allOf &&
90
+ !schema.oneOf &&
91
+ !schema.anyOf &&
92
+ !schema.if &&
93
+ !schema.then &&
94
+ !schema.else &&
95
+ !schema.required?.length &&
96
+ !schema.minProperties &&
97
+ !schema.maxProperties
98
+ )
99
+ }
100
+
101
+ /**
102
+ * Checks if a schema is likely a conceptual marker with no structural value.
103
+ */
104
+ private isConceptualMarkerSchema(schema: JSONSchema7): boolean {
105
+ // Check for schemas that are just empty objects or only have allOf with references
106
+ if (this.isEmptyObjectSchema(schema)) {
107
+ return true
108
+ }
109
+
110
+ // Check for schemas that only contain a single allOf with a $ref (like ActionStatusType)
111
+ if (
112
+ schema.type === 'object' &&
113
+ schema.allOf?.length === 1 &&
114
+ typeof schema.allOf[0] === 'object' &&
115
+ '$ref' in schema.allOf[0] &&
116
+ !schema.properties &&
117
+ !schema.additionalProperties
118
+ ) {
119
+ return true
120
+ }
121
+
122
+ // Check for schemas that only have description and type but no structure
123
+ if (
124
+ schema.type === 'object' &&
125
+ schema.description &&
126
+ !schema.properties &&
127
+ !schema.additionalProperties &&
128
+ !schema.allOf &&
129
+ !schema.oneOf &&
130
+ !schema.anyOf &&
131
+ Object.keys(schema).filter((key) => !['$schema', '$id', 'title', 'description', 'type'].includes(key)).length ===
132
+ 0
133
+ ) {
134
+ return true
135
+ }
136
+
137
+ return false
138
+ }
139
+
140
+ /**
141
+ * Gets a human-readable reason why a schema was excluded.
142
+ */
143
+ getExclusionReason(schema: JSONSchema7, schemaId?: string): string {
144
+ if (schemaId && this.options.excludeSchemaIds?.includes(schemaId)) {
145
+ return `Schema ID '${schemaId}' is in the exclusion list`
146
+ }
147
+
148
+ if (this.isEmptyObjectSchema(schema)) {
149
+ return 'Schema is an empty object with no structural definition'
150
+ }
151
+
152
+ if (this.isConceptualMarkerSchema(schema)) {
153
+ return 'Schema appears to be a conceptual marker with no concrete structure'
154
+ }
155
+
156
+ return 'Schema excluded by custom filter'
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Predefined filtering strategies for common use cases.
162
+ */
163
+ export const FILTERING_STRATEGIES = {
164
+ /**
165
+ * Strategy for API modeling - excludes schemas that don't contribute to API structure.
166
+ */
167
+ API_MODELING: new SchemaFilteringStrategy({
168
+ excludeEmptyObjects: true,
169
+ excludeConceptualMarkers: true,
170
+ excludePatterns: [
171
+ /.*Enumeration$/, // Exclude *Enumeration schemas
172
+ /.*Type$/, // Exclude conceptual *Type schemas that are just markers
173
+ ],
174
+ }),
175
+
176
+ /**
177
+ * Strategy for database modeling - very strict, only includes schemas with concrete properties.
178
+ */
179
+ DATABASE_MODELING: new SchemaFilteringStrategy({
180
+ excludeEmptyObjects: true,
181
+ excludeConceptualMarkers: true,
182
+ customExclusionFilter: (schema: JSONSchema7) => {
183
+ // For database modeling, exclude anything that doesn't have properties or clear structure
184
+ return (
185
+ schema.type === 'object' &&
186
+ !schema.properties &&
187
+ !schema.additionalProperties &&
188
+ !schema.allOf?.some(
189
+ (subSchema) => typeof subSchema === 'object' && 'properties' in subSchema && subSchema.properties
190
+ )
191
+ )
192
+ },
193
+ }),
194
+
195
+ /**
196
+ * Conservative strategy - only excludes obviously empty schemas.
197
+ */
198
+ CONSERVATIVE: new SchemaFilteringStrategy({
199
+ excludeEmptyObjects: true,
200
+ }),
201
+ } as const
@@ -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({