@api-client/core 0.11.11 → 0.12.0

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 (252) hide show
  1. package/Testing.md +1 -1
  2. package/bin/plugins/events/EventPlugin.ts +61 -0
  3. package/bin/plugins/events/assert.ts +193 -0
  4. package/bin/plugins/events/types.ts +6 -0
  5. package/bin/test.ts +8 -1
  6. package/build/src/amf/AmfShapeGenerator.d.ts +6 -3
  7. package/build/src/amf/AmfShapeGenerator.d.ts.map +1 -1
  8. package/build/src/amf/AmfShapeGenerator.js +4 -1
  9. package/build/src/amf/AmfShapeGenerator.js.map +1 -1
  10. package/build/src/amf/AmfTypes.d.ts +2 -2
  11. package/build/src/amf/AmfTypes.d.ts.map +1 -1
  12. package/build/src/amf/AmfTypes.js.map +1 -1
  13. package/build/src/amf/DataValueGenerator.d.ts +15 -15
  14. package/build/src/amf/DataValueGenerator.d.ts.map +1 -1
  15. package/build/src/amf/DataValueGenerator.js +14 -14
  16. package/build/src/amf/DataValueGenerator.js.map +1 -1
  17. package/build/src/browser.d.ts +12 -9
  18. package/build/src/browser.d.ts.map +1 -1
  19. package/build/src/browser.js +11 -8
  20. package/build/src/browser.js.map +1 -1
  21. package/build/src/exceptions/attach_exception.d.ts +11 -0
  22. package/build/src/exceptions/attach_exception.d.ts.map +1 -0
  23. package/build/src/exceptions/attach_exception.js +11 -0
  24. package/build/src/exceptions/attach_exception.js.map +1 -0
  25. package/build/src/exceptions/detach_exception.d.ts +11 -0
  26. package/build/src/exceptions/detach_exception.d.ts.map +1 -0
  27. package/build/src/exceptions/detach_exception.js +11 -0
  28. package/build/src/exceptions/detach_exception.js.map +1 -0
  29. package/build/src/exceptions/remove_model_exception.d.ts +8 -0
  30. package/build/src/exceptions/remove_model_exception.d.ts.map +1 -0
  31. package/build/src/exceptions/remove_model_exception.js +8 -0
  32. package/build/src/exceptions/remove_model_exception.js.map +1 -0
  33. package/build/src/exceptions/remove_namespace_exception.d.ts +8 -0
  34. package/build/src/exceptions/remove_namespace_exception.d.ts.map +1 -0
  35. package/build/src/exceptions/remove_namespace_exception.js +8 -0
  36. package/build/src/exceptions/remove_namespace_exception.js.map +1 -0
  37. package/build/src/index.d.ts +12 -9
  38. package/build/src/index.d.ts.map +1 -1
  39. package/build/src/index.js +11 -8
  40. package/build/src/index.js.map +1 -1
  41. package/build/src/legacy.d.ts +8 -0
  42. package/build/src/legacy.d.ts.map +1 -1
  43. package/build/src/legacy.js +9 -0
  44. package/build/src/legacy.js.map +1 -1
  45. package/build/src/modeling/Bindings.d.ts +2 -2
  46. package/build/src/modeling/Bindings.d.ts.map +1 -1
  47. package/build/src/modeling/Bindings.js.map +1 -1
  48. package/build/src/modeling/DataDomain.d.ts +601 -0
  49. package/build/src/modeling/DataDomain.d.ts.map +1 -0
  50. package/build/src/modeling/DataDomain.js +1142 -0
  51. package/build/src/modeling/DataDomain.js.map +1 -0
  52. package/build/src/modeling/DataFormat.d.ts +42 -41
  53. package/build/src/modeling/DataFormat.d.ts.map +1 -1
  54. package/build/src/modeling/DataFormat.js +30 -131
  55. package/build/src/modeling/DataFormat.js.map +1 -1
  56. package/build/src/modeling/DomainAssociation.d.ts +281 -0
  57. package/build/src/modeling/DomainAssociation.d.ts.map +1 -0
  58. package/build/src/modeling/DomainAssociation.js +440 -0
  59. package/build/src/modeling/DomainAssociation.js.map +1 -0
  60. package/build/src/modeling/DomainElement.d.ts +33 -0
  61. package/build/src/modeling/DomainElement.d.ts.map +1 -0
  62. package/build/src/modeling/DomainElement.js +32 -0
  63. package/build/src/modeling/DomainElement.js.map +1 -0
  64. package/build/src/modeling/DomainEntity.d.ts +383 -0
  65. package/build/src/modeling/DomainEntity.d.ts.map +1 -0
  66. package/build/src/modeling/DomainEntity.js +718 -0
  67. package/build/src/modeling/DomainEntity.js.map +1 -0
  68. package/build/src/modeling/DomainFile.d.ts +25 -0
  69. package/build/src/modeling/DomainFile.d.ts.map +1 -0
  70. package/build/src/modeling/DomainFile.js +86 -0
  71. package/build/src/modeling/DomainFile.js.map +1 -0
  72. package/build/src/modeling/DomainImpactAnalysis.d.ts +60 -47
  73. package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -1
  74. package/build/src/modeling/DomainImpactAnalysis.js +201 -132
  75. package/build/src/modeling/DomainImpactAnalysis.js.map +1 -1
  76. package/build/src/modeling/DomainModel.d.ts +226 -0
  77. package/build/src/modeling/DomainModel.d.ts.map +1 -0
  78. package/build/src/modeling/DomainModel.js +401 -0
  79. package/build/src/modeling/DomainModel.js.map +1 -0
  80. package/build/src/modeling/DomainNamespace.d.ts +268 -0
  81. package/build/src/modeling/DomainNamespace.d.ts.map +1 -0
  82. package/build/src/modeling/DomainNamespace.js +512 -0
  83. package/build/src/modeling/DomainNamespace.js.map +1 -0
  84. package/build/src/modeling/DomainProperty.d.ts +281 -0
  85. package/build/src/modeling/DomainProperty.d.ts.map +1 -0
  86. package/build/src/modeling/DomainProperty.js +560 -0
  87. package/build/src/modeling/DomainProperty.js.map +1 -0
  88. package/build/src/modeling/DomainSerialization.d.ts +40 -0
  89. package/build/src/modeling/DomainSerialization.d.ts.map +1 -0
  90. package/build/src/modeling/DomainSerialization.js +288 -0
  91. package/build/src/modeling/DomainSerialization.js.map +1 -0
  92. package/build/src/modeling/DomainVersioning.d.ts +17 -0
  93. package/build/src/modeling/DomainVersioning.d.ts.map +1 -0
  94. package/build/src/modeling/DomainVersioning.js +124 -0
  95. package/build/src/modeling/DomainVersioning.js.map +1 -0
  96. package/build/src/modeling/GraphUtils.d.ts +8 -0
  97. package/build/src/modeling/GraphUtils.d.ts.map +1 -0
  98. package/build/src/modeling/GraphUtils.js +26 -0
  99. package/build/src/modeling/GraphUtils.js.map +1 -0
  100. package/build/src/modeling/amf/ShapeGenerator.d.ts +164 -0
  101. package/build/src/modeling/amf/ShapeGenerator.d.ts.map +1 -0
  102. package/build/src/modeling/amf/ShapeGenerator.js +492 -0
  103. package/build/src/modeling/amf/ShapeGenerator.js.map +1 -0
  104. package/build/src/modeling/{DataAssociation.d.ts → legacy/DataAssociation.d.ts} +10 -5
  105. package/build/src/modeling/legacy/DataAssociation.d.ts.map +1 -0
  106. package/build/src/modeling/{DataAssociation.js → legacy/DataAssociation.js} +9 -6
  107. package/build/src/modeling/legacy/DataAssociation.js.map +1 -0
  108. package/build/src/modeling/{DataEntity.d.ts → legacy/DataEntity.d.ts} +12 -7
  109. package/build/src/modeling/legacy/DataEntity.d.ts.map +1 -0
  110. package/build/src/modeling/{DataEntity.js → legacy/DataEntity.js} +19 -18
  111. package/build/src/modeling/legacy/DataEntity.js.map +1 -0
  112. package/build/src/modeling/{DataEntityBuilder.d.ts → legacy/DataEntityBuilder.d.ts} +3 -2
  113. package/build/src/modeling/legacy/DataEntityBuilder.d.ts.map +1 -0
  114. package/build/src/modeling/{DataEntityBuilder.js → legacy/DataEntityBuilder.js} +3 -2
  115. package/build/src/modeling/legacy/DataEntityBuilder.js.map +1 -0
  116. package/build/src/modeling/legacy/DataImpactAnalysis.d.ts +298 -0
  117. package/build/src/modeling/legacy/DataImpactAnalysis.d.ts.map +1 -0
  118. package/build/src/modeling/legacy/DataImpactAnalysis.js +441 -0
  119. package/build/src/modeling/legacy/DataImpactAnalysis.js.map +1 -0
  120. package/build/src/modeling/{DataModel.d.ts → legacy/DataModel.d.ts} +6 -4
  121. package/build/src/modeling/legacy/DataModel.d.ts.map +1 -0
  122. package/build/src/modeling/{DataModel.js → legacy/DataModel.js} +7 -6
  123. package/build/src/modeling/legacy/DataModel.js.map +1 -0
  124. package/build/src/modeling/{DataNamespace.d.ts → legacy/DataNamespace.d.ts} +22 -3
  125. package/build/src/modeling/legacy/DataNamespace.d.ts.map +1 -0
  126. package/build/src/modeling/{DataNamespace.js → legacy/DataNamespace.js} +7 -3
  127. package/build/src/modeling/legacy/DataNamespace.js.map +1 -0
  128. package/build/src/modeling/{DataProperty.d.ts → legacy/DataProperty.d.ts} +13 -5
  129. package/build/src/modeling/legacy/DataProperty.d.ts.map +1 -0
  130. package/build/src/modeling/{DataProperty.js → legacy/DataProperty.js} +8 -5
  131. package/build/src/modeling/legacy/DataProperty.js.map +1 -0
  132. package/build/src/modeling/observed.d.ts +67 -0
  133. package/build/src/modeling/observed.d.ts.map +1 -0
  134. package/build/src/modeling/observed.js +230 -0
  135. package/build/src/modeling/observed.js.map +1 -0
  136. package/build/src/modeling/types.d.ts +165 -1
  137. package/build/src/modeling/types.d.ts.map +1 -1
  138. package/build/src/modeling/types.js.map +1 -1
  139. package/build/src/models/Thing.d.ts +26 -5
  140. package/build/src/models/Thing.d.ts.map +1 -1
  141. package/build/src/models/Thing.js +193 -91
  142. package/build/src/models/Thing.js.map +1 -1
  143. package/build/src/models/kinds.d.ts +31 -6
  144. package/build/src/models/kinds.d.ts.map +1 -1
  145. package/build/src/models/kinds.js +31 -6
  146. package/build/src/models/kinds.js.map +1 -1
  147. package/build/src/models/store/DataFile.d.ts +3 -1
  148. package/build/src/models/store/DataFile.d.ts.map +1 -1
  149. package/build/src/models/store/DataFile.js +2 -0
  150. package/build/src/models/store/DataFile.js.map +1 -1
  151. package/build/src/models/types.d.ts +12 -0
  152. package/build/src/models/types.d.ts.map +1 -0
  153. package/build/src/models/types.js +2 -0
  154. package/build/src/models/types.js.map +1 -0
  155. package/build/src/runtime/store/FilesSdk.d.ts +2 -2
  156. package/build/src/runtime/store/FilesSdk.d.ts.map +1 -1
  157. package/build/src/runtime/store/FilesSdk.js.map +1 -1
  158. package/data/models/example-generator-api.json +16 -16
  159. package/package.json +20 -6
  160. package/readme.md +1 -1
  161. package/src/amf/AmfShapeGenerator.ts +7 -4
  162. package/src/amf/AmfTypes.ts +2 -2
  163. package/src/amf/DataValueGenerator.ts +21 -21
  164. package/src/exceptions/attach_exception.ts +11 -0
  165. package/src/exceptions/detach_exception.ts +11 -0
  166. package/src/exceptions/remove_model_exception.ts +8 -0
  167. package/src/exceptions/remove_namespace_exception.ts +8 -0
  168. package/src/modeling/Bindings.ts +2 -2
  169. package/src/modeling/DataDomain.ts +1221 -0
  170. package/src/modeling/DataFormat.ts +54 -163
  171. package/src/modeling/DomainAssociation.ts +476 -0
  172. package/src/modeling/DomainElement.ts +50 -0
  173. package/src/modeling/DomainEntity.ts +769 -0
  174. package/src/modeling/DomainFile.ts +94 -0
  175. package/src/modeling/DomainImpactAnalysis.ts +218 -144
  176. package/src/modeling/DomainModel.ts +421 -0
  177. package/src/modeling/DomainNamespace.ts +537 -0
  178. package/src/modeling/DomainProperty.ts +548 -0
  179. package/src/modeling/DomainSerialization.ts +312 -0
  180. package/src/modeling/DomainVersioning.ts +144 -0
  181. package/src/modeling/GraphUtils.ts +28 -0
  182. package/src/modeling/amf/ShapeGenerator.ts +552 -0
  183. package/src/modeling/graph.md +115 -0
  184. package/src/modeling/{DataAssociation.ts → legacy/DataAssociation.ts} +13 -8
  185. package/src/modeling/{DataEntity.ts → legacy/DataEntity.ts} +28 -23
  186. package/src/modeling/{DataEntityBuilder.ts → legacy/DataEntityBuilder.ts} +4 -3
  187. package/src/modeling/legacy/DataImpactAnalysis.ts +530 -0
  188. package/src/modeling/{DataModel.ts → legacy/DataModel.ts} +10 -8
  189. package/src/modeling/{DataNamespace.ts → legacy/DataNamespace.ts} +23 -5
  190. package/src/modeling/{DataProperty.ts → legacy/DataProperty.ts} +15 -7
  191. package/src/modeling/observed.ts +267 -0
  192. package/src/modeling/types.ts +174 -1
  193. package/src/models/Thing.ts +70 -5
  194. package/src/models/kinds.ts +32 -6
  195. package/src/models/store/DataFile.ts +3 -1
  196. package/src/models/types.ts +11 -0
  197. package/src/runtime/store/FilesSdk.ts +2 -2
  198. package/tests/unit/amf/data_value_generator.spec.ts +15 -15
  199. package/tests/unit/legacy-transformers/ARC-legacy-import.spec.ts +1 -1
  200. package/tests/unit/modeling/amf/shape_generator.spec.ts +1174 -0
  201. package/tests/unit/modeling/data_domain.spec.ts +444 -0
  202. package/tests/unit/modeling/data_domain_associations.spec.ts +279 -0
  203. package/tests/unit/modeling/data_domain_change_observers.spec.ts +681 -0
  204. package/tests/unit/modeling/data_domain_entities.spec.ts +449 -0
  205. package/tests/unit/modeling/data_domain_foreign.spec.ts +355 -0
  206. package/tests/unit/modeling/data_domain_models.spec.ts +658 -0
  207. package/tests/unit/modeling/data_domain_namespaces.spec.ts +668 -0
  208. package/tests/unit/modeling/data_domain_property.spec.ts +264 -0
  209. package/tests/unit/modeling/data_domain_serialization.spec.ts +294 -0
  210. package/tests/unit/modeling/domain.property.spec.ts +822 -0
  211. package/tests/unit/modeling/domain_asociation.spec.ts +643 -0
  212. package/tests/unit/modeling/domain_asociation_targets.spec.ts +350 -0
  213. package/tests/unit/modeling/domain_entity.spec.ts +730 -0
  214. package/tests/unit/modeling/domain_entity_associations.spec.ts +330 -0
  215. package/tests/unit/modeling/domain_entity_example_generator_json.spec.ts +988 -0
  216. package/tests/unit/modeling/domain_entity_example_generator_xml.spec.ts +1451 -0
  217. package/tests/unit/modeling/domain_entity_fields.spec.ts +113 -0
  218. package/tests/unit/modeling/domain_entity_generators.spec.ts +20 -0
  219. package/tests/unit/modeling/domain_entity_parents.spec.ts +291 -0
  220. package/tests/unit/modeling/domain_entity_properties.spec.ts +305 -0
  221. package/tests/unit/modeling/{data_file.spec.ts → domain_file.spec.ts} +29 -85
  222. package/tests/unit/modeling/domain_impact_analysis.spec.ts +452 -0
  223. package/tests/unit/modeling/domain_model.spec.ts +568 -0
  224. package/tests/unit/modeling/domain_model_entities.spec.ts +408 -0
  225. package/tests/unit/modeling/domain_namespace.spec.ts +514 -0
  226. package/tests/unit/modeling/domain_namespace_models.spec.ts +324 -0
  227. package/tests/unit/modeling/domain_namespace_namespaces.spec.ts +404 -0
  228. package/tests/unit/modeling/domain_versioning.spec.ts +140 -0
  229. package/tests/unit/{amf → modeling/legacy}/amf_shape_generator.spec.ts +11 -11
  230. package/tests/unit/modeling/{data_association.spec.ts → legacy/data_association.spec.ts} +3 -11
  231. package/tests/unit/modeling/{data_entity.spec.ts → legacy/data_entity.spec.ts} +10 -8
  232. package/tests/unit/modeling/{data_entity_generator_json.spec.ts → legacy/data_entity_generator_json.spec.ts} +1 -1
  233. package/tests/unit/modeling/{data_entity_generator_xml.spec.ts → legacy/data_entity_generator_xml.spec.ts} +1 -1
  234. package/tests/unit/modeling/{data_model.spec.ts → legacy/data_model.spec.ts} +3 -3
  235. package/tests/unit/modeling/{data_namespace.spec.ts → legacy/data_namespace.spec.ts} +3 -10
  236. package/tests/unit/modeling/{data_property.spec.ts → legacy/data_property.spec.ts} +3 -6
  237. package/tests/unit/modeling/{impact_analysis.spec.ts → legacy/impact_analysis.spec.ts} +9 -9
  238. package/tests/unit/models/File/new.spec.ts +1 -1
  239. package/tests/unit/models/HttpProject.spec.ts +3 -3
  240. package/tsconfig.node.json +35 -0
  241. package/build/src/modeling/DataAssociation.d.ts.map +0 -1
  242. package/build/src/modeling/DataAssociation.js.map +0 -1
  243. package/build/src/modeling/DataEntity.d.ts.map +0 -1
  244. package/build/src/modeling/DataEntity.js.map +0 -1
  245. package/build/src/modeling/DataEntityBuilder.d.ts.map +0 -1
  246. package/build/src/modeling/DataEntityBuilder.js.map +0 -1
  247. package/build/src/modeling/DataModel.d.ts.map +0 -1
  248. package/build/src/modeling/DataModel.js.map +0 -1
  249. package/build/src/modeling/DataNamespace.d.ts.map +0 -1
  250. package/build/src/modeling/DataNamespace.js.map +0 -1
  251. package/build/src/modeling/DataProperty.d.ts.map +0 -1
  252. package/build/src/modeling/DataProperty.js.map +0 -1
@@ -0,0 +1,552 @@
1
+ import { type AssociationBindings, type PropertyWebBindings } from '../Bindings.js'
2
+ import type { DomainAssociation } from '../DomainAssociation.js'
3
+ import type { DomainProperty } from '../DomainProperty.js'
4
+ import type { DomainEntity } from '../DomainEntity.js'
5
+ import { IAmfExampleTypes, modelTypeToAmfDataType } from '../../amf/AmfTypes.js'
6
+ import { AmfNamespace } from '../../amf/definitions/Namespace.js'
7
+ import {
8
+ anyShape,
9
+ arrayShape,
10
+ fileShape,
11
+ IApiAnyShape,
12
+ IApiArrayShape,
13
+ IApiDataExample,
14
+ IApiFileShape,
15
+ IApiNodeShape,
16
+ IApiPropertyShape,
17
+ IApiRecursiveShape,
18
+ IApiScalarShape,
19
+ IApiShape,
20
+ IApiUnionShape,
21
+ IShapeUnion,
22
+ nodeShape,
23
+ propertyShape,
24
+ recursiveShape,
25
+ scalarShape,
26
+ unionShape,
27
+ } from '../../amf/definitions/Shapes.js'
28
+ import { AmfDataNode } from '../../amf/models/AmfDataNode.js'
29
+ import { DataValueGenerator } from '../../amf/DataValueGenerator.js'
30
+ import { nanoid } from 'nanoid'
31
+ import { DomainEntityKind, DomainPropertyKind } from '../../models/kinds.js'
32
+ import type { PropertySchema } from '../types.js'
33
+
34
+ const UNION_TYPE_ANY_OF = 'anyOf'
35
+ const UNION_TYPE_ALL_OF = 'allOf'
36
+ const UNION_TYPE_ONE_OF = 'oneOf'
37
+ const UNION_TYPE_NOT = 'not'
38
+ const DATA_TYPE_STRING = 'string'
39
+ const DATA_TYPE_BINARY = 'binary'
40
+
41
+ /**
42
+ * Serializes Data Domain elements (DomainEntity, DomainProperty, DomainAssociation)
43
+ * to AMF shapes.
44
+ *
45
+ * This class provides a mechanism to translate a data domain model, defined using
46
+ * custom classes like `DomainEntity`, `DomainProperty`, and `DomainAssociation`,
47
+ * into AMF (API Modeling Framework) shapes. AMF shapes are a standardized way
48
+ * to represent API structures and data models, enabling interoperability and tooling.
49
+ *
50
+ * This is particularly useful when you have a custom data model and need to
51
+ * generate API definitions (e.g., RAML or OAS) from it.
52
+ *
53
+ * **Key Features:**
54
+ *
55
+ * - **Data Domain to AMF Translation:** Converts data domain elements into
56
+ * their corresponding AMF shape representations.
57
+ * - **Recursive Shape Handling:** Detects and handles recursive relationships
58
+ * within the data model to prevent infinite loops during shape generation.
59
+ * - **Union Type Support:** Supports the creation of union types in AMF
60
+ * to represent associations that can point to multiple different entity types.
61
+ * - **Binding Integration:** Leverages binding information (e.g., web bindings)
62
+ * to customize the generated AMF shapes based on the target API format.
63
+ * - **Example and Default Value Generation:** Integrates with a `DataValueGenerator`
64
+ * to automatically generate example values and default values for properties
65
+ * within the AMF shapes.
66
+ *
67
+ * **Usage:**
68
+ *
69
+ * 1. Create an instance of the `ShapeGenerator`.
70
+ * 2. Call the appropriate method (e.g., `entity()`, `property()`, `associationShape()`)
71
+ * to generate the AMF shape for a specific data domain element.
72
+ * 3. The returned AMF shape can then be used with other AMF tools to generate
73
+ * API definitions or perform other API-related tasks.
74
+ */
75
+ export class ShapeGenerator {
76
+ #valueGenerator?: DataValueGenerator
77
+
78
+ /**
79
+ * Used with data domain value generation for entity properties.
80
+ * Lazily loaded class only when functions are used.
81
+ * This needs to be a class instances because some functions are incremental
82
+ */
83
+ get valueGenerator(): DataValueGenerator {
84
+ // we need a new instance of the generator for each shape generation
85
+ // as the generator is stateful and we need to reset it for each shape.
86
+ if (!this.#valueGenerator) {
87
+ this.#valueGenerator = new DataValueGenerator()
88
+ }
89
+ return this.#valueGenerator
90
+ }
91
+
92
+ /**
93
+ * Serializes a `DomainEntity` to an AMF node shape.
94
+ *
95
+ * This method is the entry point for converting a `DomainEntity` into its
96
+ * AMF representation. It handles recursive relationships, property serialization,
97
+ * and inheritance from parent entities.
98
+ *
99
+ * @param input The `DomainEntity` to serialize.
100
+ * @param visited A `Set` of keys of already generated entities. This prevents
101
+ * recursive shape generation and avoids infinite loops. Defaults to a new empty `Set`.
102
+ * @returns An `IApiNodeShape` or `IApiRecursiveShape` representing the entity in AMF.
103
+ * Returns a `IApiRecursiveShape` if a recursive loop is detected.
104
+ * @example
105
+ * ```typescript
106
+ * const generator = new ShapeGenerator();
107
+ * const amfShape = generator.entity(myDomainEntity);
108
+ * ```
109
+ */
110
+ entity(input: DomainEntity, visited: Set<string> = new Set<string>()): IApiNodeShape | IApiRecursiveShape {
111
+ if (visited.has(input.key)) {
112
+ // create a recursive shape.
113
+ return this.createRecursiveShape(input)
114
+ }
115
+ visited.add(input.key)
116
+ const result = nodeShape(input.key)
117
+ result.id = input.key
118
+ this.updateBaseProperties(input, result)
119
+ result.properties = []
120
+ for (const item of input.listProperties()) {
121
+ // we check whether the property is hidden.
122
+ // This is not happening when calling the `property()` because this method
123
+ // always returns the AMF shape.
124
+ const wb = item.readBinding('web') as PropertyWebBindings | undefined
125
+ if (wb && wb.hidden) {
126
+ continue
127
+ }
128
+ const shape = this.property(item)
129
+ result.properties.push(shape)
130
+ }
131
+ for (const assoc of input.listAssociations()) {
132
+ const wb = assoc.readBinding('web') as AssociationBindings | undefined
133
+ if (wb && wb.hidden) {
134
+ continue
135
+ }
136
+ const prop = this.associationProperty(assoc, visited)
137
+ result.properties.push(prop)
138
+ }
139
+ for (const parent of input.listParents()) {
140
+ const shape = this.entity(parent, visited)
141
+ result.inherits.push(shape)
142
+ }
143
+ return result
144
+ }
145
+
146
+ /**
147
+ * Serializes a `DomainProperty` to an AMF property shape.
148
+ *
149
+ * This method converts a `DomainProperty` into an `IApiPropertyShape`,
150
+ * defining its characteristics such as data type, required status, and
151
+ * range (the shape of the property's value).
152
+ *
153
+ * @param input The `DomainProperty` to serialize.
154
+ * @returns An `IApiPropertyShape` representing the property in AMF.
155
+ * @example
156
+ * ```typescript
157
+ * const generator = new ShapeGenerator();
158
+ * const amfShape = generator.property(myDomainProperty);
159
+ * ```
160
+ */
161
+ property(input: DomainProperty): IApiPropertyShape {
162
+ const { required, key } = input
163
+ const result = propertyShape(key)
164
+ result.path = `${AmfNamespace.aml.vocabularies.data.key}${input.info.name}`
165
+ if (required) {
166
+ result.minCount = 1
167
+ }
168
+ result.range = this.getRange(input)
169
+
170
+ // for example, Example generator needs to know the name of the property
171
+ // as it does not look into the "range" object.
172
+ this.updateBaseProperties(input, result)
173
+
174
+ // sync the name of the property shape with the range, in case it was changed by the bindings
175
+ if (result.range.name) {
176
+ result.name = result.range.name
177
+ }
178
+ return result
179
+ }
180
+
181
+ /**
182
+ * Serializes a `DomainAssociation` to an AMF property shape, handling the
183
+ * association's target entity or entities.
184
+ *
185
+ * This method is similar to `property()`, but it specifically handles
186
+ * associations between entities. It determines the range of the property
187
+ * based on the association's target(s) and generates the appropriate
188
+ * AMF shape (e.g., a scalar shape for a linked schema, a union shape for
189
+ * multiple targets).
190
+ *
191
+ * @param input The `DomainAssociation` to serialize.
192
+ * @param visited A `Set` of keys of already generated entities. This prevents
193
+ * recursive shape generation and avoids infinite loops. Defaults to a new empty `Set`.
194
+ * @returns An `IApiPropertyShape` representing the association in AMF.
195
+ * @example
196
+ * ```typescript
197
+ * const generator = new ShapeGenerator();
198
+ * const amfShape = generator.associationProperty(myDomainAssociation);
199
+ * ```
200
+ */
201
+ associationProperty(input: DomainAssociation, visited: Set<string> = new Set<string>()): IApiPropertyShape {
202
+ const { required, key } = input
203
+ const result = propertyShape(key)
204
+ result.path = `${AmfNamespace.aml.vocabularies.data.key}${input.info.name}`
205
+ if (required) {
206
+ result.minCount = 1
207
+ }
208
+ result.range = this.associationShape(input, visited)
209
+
210
+ this.updateBaseProperties(input, result)
211
+ return result
212
+ }
213
+
214
+ /**
215
+ * Generates a shape for an association. Most likely you want to use the `associationProperty()` method instead.
216
+ *
217
+ * @param input The data association instance.
218
+ * @param visited A `Set` of keys of already generated entities. This prevents
219
+ * recursive shape generation and avoids infinite loops. Defaults to a new empty `Set`.
220
+ * @returns The range value for the PropertyShape.
221
+ */
222
+ associationShape(input: DomainAssociation, visited: Set<string> = new Set<string>()): IShapeUnion | undefined {
223
+ const schema = input.schema
224
+ if (schema && schema.linked) {
225
+ return this.createLinkedShape(input)
226
+ }
227
+ const items = this.associationUnion(input, visited)
228
+ if (!items) {
229
+ return
230
+ }
231
+ if (Array.isArray(items)) {
232
+ return this.createUnionShape(input, items)
233
+ }
234
+ const unionType = (schema && schema.unionType) || UNION_TYPE_ANY_OF
235
+ if (unionType === UNION_TYPE_NOT) {
236
+ // Need to investigate more about "not" union type.
237
+ // AMF supports it, but I am not sure how to handle it.
238
+ const wrapper = anyShape(input.key)
239
+ wrapper.id = `not-shape-${input.key}`
240
+ wrapper.not = items
241
+ return wrapper
242
+ }
243
+ if (input.multiple) {
244
+ return this.refactorShapeToArray(input.key, items)
245
+ }
246
+ return items
247
+ }
248
+
249
+ /**
250
+ * @param input The data association instance.
251
+ * @returns An AMF API scalar shape with the link to the schema.
252
+ */
253
+ protected createLinkedShape(input: DomainAssociation): IApiScalarShape {
254
+ // This is a link to the schema. In an API that would be the id
255
+ // of a resource to request the data from.
256
+ const range = scalarShape(input.key)
257
+ range.id = `link-${input.key}`
258
+ range.dataType = modelTypeToAmfDataType(DATA_TYPE_STRING)
259
+ return range
260
+ }
261
+
262
+ protected createUnionShape(input: DomainAssociation, items: IShapeUnion[]): IApiUnionShape | IApiArrayShape {
263
+ const range = unionShape(input.key)
264
+ this.updateBaseProperties(input, range)
265
+ range.anyOf = []
266
+ const unionType = (input.schema && input.schema.unionType) || UNION_TYPE_ANY_OF
267
+ if (unionType === UNION_TYPE_ANY_OF) {
268
+ range.anyOf = items
269
+ } else if (unionType === UNION_TYPE_ALL_OF) {
270
+ range.and = items
271
+ } else if (unionType === UNION_TYPE_ONE_OF) {
272
+ range.xone = items
273
+ } else {
274
+ // the "not" union type only supports a single schema.
275
+ // I am not sure how this should be handled. Will take the first one
276
+ // for now.
277
+ range.not = items[0]
278
+ }
279
+ if (input.multiple) {
280
+ return this.refactorShapeToArray(input.key, range)
281
+ }
282
+ return range
283
+ }
284
+
285
+ /**
286
+ * Generates a shape list for an union. Most likely you want to use the `associationProperty()` method instead.
287
+ *
288
+ * @param input The data association instance.
289
+ * @returns The range value for the PropertyShape.
290
+ */
291
+ associationUnion(
292
+ input: DomainAssociation,
293
+ visited: Set<string> = new Set<string>()
294
+ ): IShapeUnion | IShapeUnion[] | undefined {
295
+ const result: IShapeUnion[] = []
296
+ for (const item of input.listTargets()) {
297
+ result.push(this.entity(item, visited))
298
+ }
299
+ if (!result.length) {
300
+ return undefined
301
+ }
302
+ if (result.length > 1) {
303
+ return result
304
+ }
305
+ return result[0]
306
+ }
307
+
308
+ /**
309
+ * The DomainProperty may have both the `schema` and the `bindings`. For AMF shape we read `schema` for
310
+ * default value, examples, and enum values. We also look for the `web`
311
+ * bindings for more detailed definition of a shape.
312
+ *
313
+ * @param input
314
+ */
315
+ protected getRange(input: DomainProperty): IApiArrayShape | IApiFileShape | IApiScalarShape {
316
+ const bindings = input.readBinding('web') as PropertyWebBindings | undefined
317
+ let schema: PropertySchema | undefined
318
+ if (input.schema) {
319
+ schema = input.schema
320
+ }
321
+ const { multiple, type } = input
322
+ if (multiple) {
323
+ return this.createArrayShape(input, schema, bindings)
324
+ }
325
+ if (type === DATA_TYPE_BINARY) {
326
+ return this.createFileShape(input, bindings)
327
+ }
328
+ return this.createScalarShape(input, schema, bindings)
329
+ }
330
+
331
+ /**
332
+ * Normally this would be part of generating a scalar schema but the the property is an array this
333
+ * is generated on the array and not on the range.
334
+ *
335
+ * @param result The scalar or array shape.
336
+ * @param schema The property schema
337
+ * @param type The data type of the parent property as set on the `range`
338
+ * @param isArray Whether the DomainProperty is multiple
339
+ */
340
+ protected setShapeSchema(
341
+ input: DomainProperty,
342
+ result: IApiAnyShape,
343
+ schema: PropertySchema,
344
+ type: string,
345
+ isArray?: boolean
346
+ ): void {
347
+ if (schema.defaultValue) {
348
+ const { type: dfFormat } = schema.defaultValue
349
+ const { value } = schema.defaultValue
350
+ if (dfFormat === 'function') {
351
+ const tmp = this.valueGenerator.generate(value, input.type)
352
+ if (tmp !== DataValueGenerator.noValue) {
353
+ const dt = AmfDataNode.scalar(tmp as string, dfFormat)
354
+ result.defaultValue = dt.toJSON()
355
+ }
356
+ } else {
357
+ const dt = AmfDataNode.scalar(value, type)
358
+ result.defaultValue = dt.toJSON()
359
+ }
360
+ }
361
+ if (Array.isArray(schema.enum)) {
362
+ result.values = schema.enum.map((i) => AmfDataNode.scalar(i, type).toJSON())
363
+ }
364
+ if (Array.isArray(schema.examples)) {
365
+ if (isArray) {
366
+ result.examples = this.createArrayExamples(schema.examples, type)
367
+ } else {
368
+ result.examples = this.createExamples(schema.examples, type)
369
+ }
370
+ }
371
+ }
372
+
373
+ protected createArrayShape(
374
+ input: DomainProperty,
375
+ schema?: PropertySchema,
376
+ bindings?: PropertyWebBindings
377
+ ): IApiArrayShape {
378
+ const result = arrayShape(input.key)
379
+ const { type } = input
380
+ if (type === DATA_TYPE_BINARY) {
381
+ // we do not pass schema to the range generator as we set schema's properties on the array shape.
382
+ result.items = this.createFileShape(input, bindings)
383
+ } else {
384
+ result.items = this.createScalarShape(input, undefined, bindings)
385
+ }
386
+ if (schema) {
387
+ const type = (result.items as IApiScalarShape).dataType as string
388
+ this.setShapeSchema(input, result, schema, type, input.multiple)
389
+ }
390
+ return result
391
+ }
392
+
393
+ protected createScalarShape(
394
+ input: DomainProperty,
395
+ schema?: PropertySchema,
396
+ bindings?: PropertyWebBindings
397
+ ): IApiScalarShape {
398
+ const result = scalarShape(input.key)
399
+ this.updateBaseProperties(input, result)
400
+ this.setScalarCommonProperties(result, input, bindings)
401
+ if (!result.dataType) {
402
+ result.dataType = modelTypeToAmfDataType(input.type, bindings)
403
+ }
404
+ if (schema) {
405
+ this.setShapeSchema(input, result, schema, result.dataType as string, input.multiple)
406
+ }
407
+ return result
408
+ }
409
+
410
+ protected createExamples(examples: string[], type: string): IApiDataExample[] {
411
+ const result: IApiDataExample[] = []
412
+ for (const current of examples) {
413
+ const item: IApiDataExample = {
414
+ id: nanoid(),
415
+ customDomainProperties: [],
416
+ strict: true,
417
+ types: IAmfExampleTypes,
418
+ structuredValue: AmfDataNode.scalar(current, type).toJSON(),
419
+ }
420
+ result.push(item)
421
+ }
422
+ return result
423
+ }
424
+
425
+ protected createArrayExamples(examples: string[], type: string): IApiDataExample[] {
426
+ const item: IApiDataExample = {
427
+ id: nanoid(),
428
+ customDomainProperties: [],
429
+ strict: true,
430
+ types: IAmfExampleTypes,
431
+ }
432
+ const value = new AmfDataNode('array')
433
+ for (const item of examples) {
434
+ const member = AmfDataNode.scalar(item, type)
435
+ value.addMember(member)
436
+ }
437
+ item.structuredValue = value.toJSON()
438
+ return [item]
439
+ }
440
+
441
+ protected createFileShape(input: DomainProperty, bindings?: PropertyWebBindings): IApiFileShape {
442
+ const result = fileShape(input.key)
443
+ this.updateBaseProperties(input, result)
444
+ if (bindings) {
445
+ if (Array.isArray(bindings.fileTypes)) {
446
+ result.fileTypes = bindings.fileTypes
447
+ }
448
+ this.setScalarCommonProperties(result, input, bindings)
449
+ if (bindings.format === 'base64') {
450
+ result.format = AmfNamespace.w3.xmlSchema.base64Binary
451
+ }
452
+ }
453
+ return result
454
+ }
455
+
456
+ protected setScalarCommonProperties(
457
+ result: IApiFileShape | IApiScalarShape,
458
+ input: DomainProperty,
459
+ bindings?: PropertyWebBindings
460
+ ): void {
461
+ if (bindings?.name) {
462
+ result.name = bindings.name
463
+ }
464
+ if (bindings?.xml) {
465
+ result.xmlSerialization = bindings.xml
466
+ }
467
+ if (bindings?.pattern) {
468
+ result.pattern = bindings.pattern
469
+ }
470
+ const { schema, type } = input
471
+ if (schema) {
472
+ if (typeof schema.multipleOf === 'number') {
473
+ result.multipleOf = schema.multipleOf
474
+ }
475
+ if (typeof schema.minimum === 'number') {
476
+ if (type === DATA_TYPE_STRING) {
477
+ result.minLength = schema.minimum
478
+ } else {
479
+ result.minimum = schema.minimum
480
+ }
481
+ }
482
+ if (typeof schema.maximum === 'number') {
483
+ if (type === DATA_TYPE_STRING) {
484
+ result.maxLength = schema.maximum
485
+ } else {
486
+ result.maximum = schema.maximum
487
+ }
488
+ }
489
+ if (typeof schema.exclusiveMinimum === 'boolean') {
490
+ result.exclusiveMinimum = schema.exclusiveMinimum
491
+ }
492
+ if (typeof schema.exclusiveMaximum === 'boolean') {
493
+ result.exclusiveMaximum = schema.exclusiveMaximum
494
+ }
495
+ }
496
+ if (bindings?.format) {
497
+ switch (bindings.format) {
498
+ case 'base64':
499
+ result.format = AmfNamespace.w3.xmlSchema.base64Binary
500
+ break
501
+ case 'double':
502
+ result.format = AmfNamespace.w3.xmlSchema.double
503
+ break
504
+ case 'float':
505
+ result.format = AmfNamespace.w3.xmlSchema.float
506
+ break
507
+ case 'int32':
508
+ result.format = AmfNamespace.w3.xmlSchema.integer
509
+ break
510
+ case 'int64':
511
+ result.format = AmfNamespace.w3.xmlSchema.integer
512
+ break
513
+ }
514
+ // result.format = bindings.format
515
+ }
516
+ if (typeof input.readOnly === 'boolean') {
517
+ result.readOnly = input.readOnly
518
+ }
519
+ if (typeof input.writeOnly === 'boolean') {
520
+ result.writeOnly = input.writeOnly
521
+ }
522
+ }
523
+
524
+ protected updateBaseProperties(input: DomainProperty | DomainAssociation | DomainEntity, target: IApiShape): void {
525
+ target.name = input.info.name
526
+ target.displayName = input.info.displayName
527
+ target.description = input.info.description
528
+ if (input.kind === DomainPropertyKind) {
529
+ target.deprecated = (input as DomainProperty).deprecated
530
+ } else if (input.kind === DomainEntityKind) {
531
+ target.deprecated = (input as DomainEntity).deprecated
532
+ }
533
+ }
534
+
535
+ protected createRecursiveShape(input: DomainEntity): IApiRecursiveShape {
536
+ return recursiveShape(input.key, input.key)
537
+ }
538
+
539
+ /**
540
+ * Translates the shape to an array shape. This happens when data model property
541
+ * is changed from 'multiple' to not-multiple and back.
542
+ *
543
+ * @param id The key of the parameter or an association
544
+ * @param shape The shape to wrap as an array.
545
+ * @returns Array shape.
546
+ */
547
+ protected refactorShapeToArray(id: string, shape: IShapeUnion): IApiArrayShape {
548
+ const result = arrayShape(id)
549
+ result.items = shape
550
+ return result
551
+ }
552
+ }
@@ -0,0 +1,115 @@
1
+ # Using graphs
2
+
3
+ ## Conceptual Breakdown of the Data Domain
4
+
5
+ ```plain
6
+ DataDomain (Root container)
7
+ ├── DataNamespace (logical grouping)
8
+ │ └── DataModel
9
+ │ └── DataEntity
10
+ │ ├── Property (name, type, ...)
11
+ │ ├── Association (name, cardinality, target Entity)
12
+ │ └── ParentEntity (reference to another DataEntity)
13
+ ├── DataModel
14
+ │ └── DataEntity
15
+ │ ├── Property
16
+ │ ├── Association
17
+ │ └── ParentEntity
18
+ └── ForeignDataNamespace (immutable, imported from another DataDomain)
19
+ └── DataModel
20
+ └── DataEntity
21
+ ├── Property
22
+ ├── Association
23
+ └── ParentEntity
24
+ ```
25
+
26
+ ## Graph Structure and Relationships
27
+
28
+ The data domain is represented as a directed graph where nodes represent the core elements (namespaces, models, entities, properties, and associations) and edges represent the relationships between them.
29
+
30
+ Here's a breakdown of how each element is represented in the graph:
31
+
32
+ **Nodes:**
33
+
34
+ * **Namespace:** Represents a `DataNamespace`. It's a container for models and other namespaces.
35
+ * **Model:** Represents a `DataModel`. It's a container for entities.
36
+ * **Entity:** Represents a `DataEntity`. It's the fundamental building block, defining a specific type of data.
37
+ * **Property:** Represents a `DataProperty`. It's an attribute of an entity.
38
+ * **Association:** Represents a `DataAssociation`. It defines a relationship between entities.
39
+
40
+ **Edges:**
41
+
42
+ Edges define the relationships between nodes. Each edge can have a `type` label to specify the kind of relationship.
43
+
44
+ * **Namespace Hierarchy:**
45
+ * A namespace can be a child of another namespace, creating a hierarchical structure.
46
+ * **Edge:** No edges. We use the graph's parent-child logic.
47
+ * **Cardinality:** A namespace can have only one parent namespace.
48
+ * **Type:** No specific type, just a connection.
49
+ * **Namespace-Model:**
50
+ * A model can be a child of a namespace. By default it is a child of a Data Domain.
51
+ * **Edge:** No edges. We use the graph's parent-child logic.
52
+ * **Cardinality:** A model can have only one parent namespace.
53
+ * **Type:** No specific type, just a connection.
54
+ * **Model-Entity:**
55
+ * An entity is always a child of a model.
56
+ * **Edge:** No edges. We use the graph's parent-child logic.
57
+ * **Cardinality:** An entity can have only one parent model.
58
+ * **Type:** No specific type, just a connection.
59
+ * **Entity Inheritance (Parent):**
60
+ * An entity can inherit from other entities.
61
+ * **Edge:** An edge exists from the child entity to the parent entity.
62
+ * **Cardinality:** An entity can have multiple parent entities.
63
+ * **Type:** `{ type: 'parent' }`
64
+ * **Entity-Property:**
65
+ * An entity has properties that describe its attributes.
66
+ * **Edge:** An edge exists from the entity to the property.
67
+ * **Cardinality:** An entity can have multiple properties.
68
+ * **Type:** `{ type: 'property' }`
69
+ * **Entity-Association:**
70
+ * An entity can have associations to other entities (including itself).
71
+ * **Edge 1:** An edge exists from the entity to the association node.
72
+ * **Edge 2:** An edge exists from the association node to each target entity.
73
+ * **Cardinality:** An entity can have multiple associations, and each association can have multiple target entities.
74
+ * **Type:** `{ type: 'association' }`
75
+
76
+ ## Conceptual Graph Visualization
77
+
78
+ The following diagram illustrates the relationships described above:
79
+
80
+ ```mermaid
81
+ graph TD
82
+ subgraph "Namespaces"
83
+ N1[Namespace 1]
84
+ N2[Namespace 2]
85
+ end
86
+ subgraph "Models"
87
+ M1[Model 1]
88
+ M2[Model 2]
89
+ end
90
+ subgraph "Entities"
91
+ E1[Entity 1]
92
+ E2[Entity 2]
93
+ E3[Entity 3]
94
+ end
95
+ subgraph "Properties"
96
+ P1[Property 1]
97
+ P2[Property 2]
98
+ end
99
+ subgraph "Associations"
100
+ A1[Association 1]
101
+ end
102
+
103
+ N2 --> N1
104
+ N1 --> M1
105
+ N2 --> M2
106
+ M1 --> E1
107
+ M1 --> E2
108
+ M2 --> E3
109
+ E1 -- "{ type: 'parent' }" --> E2
110
+ E1 -- "{ type: 'property' }" --> P1
111
+ E1 -- "{ type: 'property' }" --> P2
112
+ E1 -- "{ type: 'association' }" --> A1
113
+ A1 -- "{ type: 'association' }" --> E2
114
+ A1 -- "{ type: 'association' }" --> E3
115
+ ```