@api-client/core 0.14.0 → 0.14.2
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/amf/AmfTypes.d.ts +1 -1
- package/build/src/amf/AmfTypes.js +1 -1
- package/build/src/amf/AmfTypes.js.map +1 -1
- package/build/src/amf/Utils.d.ts +0 -6
- package/build/src/amf/Utils.d.ts.map +1 -1
- package/build/src/amf/Utils.js +0 -14
- package/build/src/amf/Utils.js.map +1 -1
- package/build/src/browser.d.ts +2 -1
- package/build/src/browser.d.ts.map +1 -1
- package/build/src/browser.js +1 -0
- package/build/src/browser.js.map +1 -1
- package/build/src/index.d.ts +2 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +1 -0
- package/build/src/index.js.map +1 -1
- package/build/src/legacy.d.ts +0 -8
- package/build/src/legacy.d.ts.map +1 -1
- package/build/src/legacy.js +0 -9
- package/build/src/legacy.js.map +1 -1
- package/build/src/modeling/Bindings.d.ts +1 -1
- package/build/src/modeling/Bindings.js.map +1 -1
- package/build/src/modeling/DataDomain.js +2 -2
- package/build/src/modeling/DataDomain.js.map +1 -1
- package/build/src/modeling/DataFormat.d.ts +0 -40
- package/build/src/modeling/DataFormat.d.ts.map +1 -1
- package/build/src/modeling/DataFormat.js +0 -27
- package/build/src/modeling/DataFormat.js.map +1 -1
- package/build/src/modeling/DomainAssociation.d.ts +63 -0
- package/build/src/modeling/DomainAssociation.d.ts.map +1 -1
- package/build/src/modeling/DomainAssociation.js +110 -4
- package/build/src/modeling/DomainAssociation.js.map +1 -1
- package/build/src/modeling/DomainEntity.d.ts +25 -9
- package/build/src/modeling/DomainEntity.d.ts.map +1 -1
- package/build/src/modeling/DomainEntity.js +65 -21
- package/build/src/modeling/DomainEntity.js.map +1 -1
- package/build/src/modeling/DomainFile.d.ts +1 -1
- package/build/src/modeling/DomainFile.js +1 -1
- package/build/src/modeling/DomainFile.js.map +1 -1
- package/build/src/modeling/DomainImpactAnalysis.d.ts +1 -1
- package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -1
- package/build/src/modeling/DomainImpactAnalysis.js +3 -3
- package/build/src/modeling/DomainImpactAnalysis.js.map +1 -1
- package/build/src/modeling/DomainModel.d.ts +2 -2
- package/build/src/modeling/DomainModel.js +2 -2
- package/build/src/modeling/DomainModel.js.map +1 -1
- package/build/src/modeling/DomainProperty.d.ts +28 -12
- package/build/src/modeling/DomainProperty.d.ts.map +1 -1
- package/build/src/modeling/DomainProperty.js +61 -26
- package/build/src/modeling/DomainProperty.js.map +1 -1
- package/build/src/modeling/Semantics.d.ts +117 -0
- package/build/src/modeling/Semantics.d.ts.map +1 -0
- package/build/src/modeling/Semantics.js +112 -0
- package/build/src/modeling/Semantics.js.map +1 -0
- package/build/src/models/kinds.d.ts +0 -24
- package/build/src/models/kinds.d.ts.map +1 -1
- package/build/src/models/kinds.js +0 -24
- package/build/src/models/kinds.js.map +1 -1
- package/build/src/models/store/data_catalog.d.ts +1 -1
- package/build/src/models/store/data_catalog.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/APIC-187.json +3 -3
- package/data/models/APIC-188.json +3 -3
- package/data/models/APIC-233.json +1 -1
- package/data/models/APIC-391.json +2 -2
- package/data/models/APIC-483.json +1 -1
- package/data/models/APIC-487.json +1 -1
- package/data/models/APIC-655.json +1 -1
- package/data/models/APIC-689.json +1 -1
- package/data/models/APIC-690.json +5 -5
- package/data/models/SE-10469.json +1 -1
- package/data/models/SE-13092.json +5 -5
- package/data/models/SE-22063.json +12 -2
- package/data/models/amf-helper-api.json +154 -14
- package/data/models/arc-demo-api.json +95 -15
- package/data/models/async-api.json +1 -1
- package/data/models/example-generator-api.json +369 -29
- package/data/models/expanded-api.json +1 -1
- package/data/models/flattened-api.json +1 -1
- package/data/models/multiple-servers.json +1 -1
- package/data/models/oas-3-api.json +1 -1
- package/data/models/oas-date.json +1 -1
- package/data/models/oas-types.json +1 -1
- package/data/models/oas-unions.json +1 -1
- package/data/models/petstore.json +1 -1
- package/data/models/raml-date.json +1 -1
- package/data/models/recursive.json +1 -1
- package/data/models/schema-api.json +62 -2
- package/data/models/secured-api.json +16 -16
- package/data/models/tracked-to-linked.json +4 -4
- package/package.json +2 -2
- package/src/amf/AmfTypes.ts +1 -1
- package/src/amf/Utils.ts +0 -15
- package/src/modeling/Bindings.ts +1 -1
- package/src/modeling/DataDomain.ts +2 -2
- package/src/modeling/DataFormat.ts +0 -48
- package/src/modeling/DomainAssociation.ts +122 -3
- package/src/modeling/DomainEntity.ts +56 -17
- package/src/modeling/DomainFile.ts +1 -1
- package/src/modeling/DomainImpactAnalysis.ts +3 -3
- package/src/modeling/DomainModel.ts +2 -2
- package/src/modeling/DomainProperty.ts +60 -21
- package/src/modeling/Semantics.ts +195 -0
- package/src/modeling/graph.md +14 -14
- package/src/modeling/readme.md +29 -29
- package/src/models/kinds.ts +0 -25
- package/src/models/store/data_catalog.ts +1 -1
- package/tests/unit/modeling/data_domain_change_observers.spec.ts +11 -10
- package/tests/unit/modeling/data_domain_entities.spec.ts +129 -1
- package/tests/unit/modeling/data_domain_property.spec.ts +1 -1
- package/tests/unit/modeling/domain_asociation.spec.ts +267 -0
- package/tests/unit/modeling/domain_entity.spec.ts +27 -26
- package/tests/unit/modeling/domain_entity_example_generator_json.spec.ts +11 -11
- package/tests/unit/modeling/domain_entity_example_generator_xml.spec.ts +10 -10
- package/tests/unit/modeling/{domain.property.spec.ts → domain_property.spec.ts} +139 -23
- package/tests/unit/modeling/semantics.spec.ts +149 -0
- package/build/src/amf/AmfShapeGenerator.d.ts +0 -103
- package/build/src/amf/AmfShapeGenerator.d.ts.map +0 -1
- package/build/src/amf/AmfShapeGenerator.js +0 -416
- package/build/src/amf/AmfShapeGenerator.js.map +0 -1
- package/build/src/modeling/legacy/DataAssociation.d.ts +0 -284
- package/build/src/modeling/legacy/DataAssociation.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataAssociation.js +0 -443
- package/build/src/modeling/legacy/DataAssociation.js.map +0 -1
- package/build/src/modeling/legacy/DataEntity.d.ts +0 -358
- package/build/src/modeling/legacy/DataEntity.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataEntity.js +0 -855
- package/build/src/modeling/legacy/DataEntity.js.map +0 -1
- package/build/src/modeling/legacy/DataEntityBuilder.d.ts +0 -162
- package/build/src/modeling/legacy/DataEntityBuilder.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataEntityBuilder.js +0 -221
- package/build/src/modeling/legacy/DataEntityBuilder.js.map +0 -1
- package/build/src/modeling/legacy/DataImpactAnalysis.d.ts +0 -298
- package/build/src/modeling/legacy/DataImpactAnalysis.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataImpactAnalysis.js +0 -441
- package/build/src/modeling/legacy/DataImpactAnalysis.js.map +0 -1
- package/build/src/modeling/legacy/DataModel.d.ts +0 -99
- package/build/src/modeling/legacy/DataModel.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataModel.js +0 -237
- package/build/src/modeling/legacy/DataModel.js.map +0 -1
- package/build/src/modeling/legacy/DataNamespace.d.ts +0 -340
- package/build/src/modeling/legacy/DataNamespace.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataNamespace.js +0 -784
- package/build/src/modeling/legacy/DataNamespace.js.map +0 -1
- package/build/src/modeling/legacy/DataProperty.d.ts +0 -332
- package/build/src/modeling/legacy/DataProperty.d.ts.map +0 -1
- package/build/src/modeling/legacy/DataProperty.js +0 -415
- package/build/src/modeling/legacy/DataProperty.js.map +0 -1
- package/build/src/models/store/DataFile.d.ts +0 -29
- package/build/src/models/store/DataFile.d.ts.map +0 -1
- package/build/src/models/store/DataFile.js +0 -87
- package/build/src/models/store/DataFile.js.map +0 -1
- package/src/amf/AmfShapeGenerator.ts +0 -477
- package/src/modeling/legacy/DataAssociation.ts +0 -554
- package/src/modeling/legacy/DataEntity.ts +0 -1019
- package/src/modeling/legacy/DataEntityBuilder.ts +0 -236
- package/src/modeling/legacy/DataImpactAnalysis.ts +0 -530
- package/src/modeling/legacy/DataModel.ts +0 -276
- package/src/modeling/legacy/DataNamespace.ts +0 -929
- package/src/modeling/legacy/DataProperty.ts +0 -630
- package/src/models/store/DataFile.ts +0 -95
- package/tests/unit/modeling/legacy/amf_shape_generator.spec.ts +0 -1041
- package/tests/unit/modeling/legacy/data_association.spec.ts +0 -710
- package/tests/unit/modeling/legacy/data_entity.spec.ts +0 -2061
- package/tests/unit/modeling/legacy/data_entity_generator_json.spec.ts +0 -987
- package/tests/unit/modeling/legacy/data_entity_generator_xml.spec.ts +0 -1451
- package/tests/unit/modeling/legacy/data_model.spec.ts +0 -395
- package/tests/unit/modeling/legacy/data_namespace.spec.ts +0 -1312
- package/tests/unit/modeling/legacy/data_property.spec.ts +0 -887
- package/tests/unit/modeling/legacy/impact_analysis.spec.ts +0 -373
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import type { DomainPropertyType } from './DataFormat.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Defines the names for all available data semantics.
|
|
6
|
+
* Using a string enum makes it easy to add or remove semantics in a single place.
|
|
7
|
+
*/
|
|
8
|
+
export enum SemanticType {
|
|
9
|
+
// Entity-Level Semantics
|
|
10
|
+
/**
|
|
11
|
+
* Designates a Data Entity that represents users of the system.
|
|
12
|
+
*/
|
|
13
|
+
User = 'https://apinow.app/semantics/entities/#User',
|
|
14
|
+
|
|
15
|
+
// Property-Level Semantics
|
|
16
|
+
CreatedTimestamp = 'https://apinow.app/semantics/properties/#CreatedTimestamp',
|
|
17
|
+
UpdatedTimestamp = 'https://apinow.app/semantics/properties/#UpdatedTimestamp',
|
|
18
|
+
DeletedTimestamp = 'https://apinow.app/semantics/properties/#DeletedTimestamp',
|
|
19
|
+
DeletedFlag = 'https://apinow.app/semantics/properties/#DeletedFlag',
|
|
20
|
+
PublicUniqueName = 'https://apinow.app/semantics/properties/#PublicUniqueName',
|
|
21
|
+
/**
|
|
22
|
+
* Designates a Data Property as the `role` of a user within the system.
|
|
23
|
+
* This is used to define the user's permissions and access levels.
|
|
24
|
+
* For example, a user with the role of "admin" would have elevated permissions
|
|
25
|
+
* compared to a user with the role of "guest".
|
|
26
|
+
* Roles are defined on the entity as enums, or as a string property with a controlled vocabulary.
|
|
27
|
+
*/
|
|
28
|
+
UserRole = 'https://apinow.app/semantics/entities/#UserRole',
|
|
29
|
+
// Association-Level Semantics
|
|
30
|
+
ResourceOwnerIdentifier = 'https://apinow.app/semantics/associations/#ResourceOwnerIdentifier',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Defines the scope at which a semantic can be applied.
|
|
35
|
+
*/
|
|
36
|
+
export enum SemanticScope {
|
|
37
|
+
Entity = 'Entity',
|
|
38
|
+
Property = 'Property',
|
|
39
|
+
Association = 'Association',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A base interface for all Data Semantics, containing common properties.
|
|
44
|
+
* A semantic is an annotation applied to a Data Entity, Property, or Association
|
|
45
|
+
* to provide additional context or constraints.
|
|
46
|
+
* This interface is extended by more specific semantic types.
|
|
47
|
+
*/
|
|
48
|
+
interface BaseDataSemantic {
|
|
49
|
+
/**
|
|
50
|
+
* A unique identifier for the semantic definition.
|
|
51
|
+
*/
|
|
52
|
+
id: SemanticType
|
|
53
|
+
/**
|
|
54
|
+
* A human-readable name for the semantic.
|
|
55
|
+
*/
|
|
56
|
+
displayName: string
|
|
57
|
+
/**
|
|
58
|
+
* A description of the semantic's purpose and impact.
|
|
59
|
+
*/
|
|
60
|
+
description: string
|
|
61
|
+
/**
|
|
62
|
+
* Specifies whether the semantic applies to an Entity, Property, or Association.
|
|
63
|
+
*/
|
|
64
|
+
scope: SemanticScope
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Represents a semantic that can be applied to an entire Data Entity.
|
|
69
|
+
*/
|
|
70
|
+
export interface EntitySemantic extends BaseDataSemantic {
|
|
71
|
+
scope: SemanticScope.Entity
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Represents a semantic that can be applied to a single property.
|
|
76
|
+
*/
|
|
77
|
+
export interface PropertySemantic extends BaseDataSemantic {
|
|
78
|
+
scope: SemanticScope.Property
|
|
79
|
+
/**
|
|
80
|
+
* Optional array of data types this semantic can be applied to.
|
|
81
|
+
* Enforces constraints, e.g., DeletedTimestamp on a DateTime property.
|
|
82
|
+
*/
|
|
83
|
+
applicableDataTypes?: DomainPropertyType[]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Represents a semantic that can be applied to an association between entities.
|
|
88
|
+
*/
|
|
89
|
+
export interface AssociationSemantic extends BaseDataSemantic {
|
|
90
|
+
scope: SemanticScope.Association
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* A type guard to check if a semantic is an EntitySemantic.
|
|
95
|
+
*/
|
|
96
|
+
export const isEntitySemantic = (semantic: DataSemantic): semantic is EntitySemantic =>
|
|
97
|
+
semantic.scope === SemanticScope.Entity
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A type guard to check if a semantic is a PropertySemantic.
|
|
101
|
+
*/
|
|
102
|
+
export const isPropertySemantic = (semantic: DataSemantic): semantic is PropertySemantic =>
|
|
103
|
+
semantic.scope === SemanticScope.Property
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A type guard to check if a semantic is a AssociationSemantic.
|
|
107
|
+
*/
|
|
108
|
+
export const isAssociationSemantic = (semantic: DataSemantic): semantic is AssociationSemantic =>
|
|
109
|
+
semantic.scope === SemanticScope.Association
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Union type for any kind of data semantic
|
|
113
|
+
*/
|
|
114
|
+
export type DataSemantic = EntitySemantic | PropertySemantic | AssociationSemantic
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A map to store the definitions of all available data semantics.
|
|
118
|
+
* This acts as a central registry for the application.
|
|
119
|
+
*/
|
|
120
|
+
export const DataSemantics: Record<SemanticType, DataSemantic> = {
|
|
121
|
+
// Entity-Level Definitions
|
|
122
|
+
[SemanticType.User]: {
|
|
123
|
+
id: SemanticType.User,
|
|
124
|
+
displayName: 'User Entity',
|
|
125
|
+
scope: SemanticScope.Entity,
|
|
126
|
+
description: 'Designates an entity that represents system users, crucial for authentication and authorization.',
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
// Property-Level Definitions
|
|
130
|
+
[SemanticType.CreatedTimestamp]: {
|
|
131
|
+
id: SemanticType.CreatedTimestamp,
|
|
132
|
+
displayName: 'Creation Timestamp',
|
|
133
|
+
scope: SemanticScope.Property,
|
|
134
|
+
description: "Marks a field as the one that contains the object's creation timestamp.",
|
|
135
|
+
applicableDataTypes: ['datetime'],
|
|
136
|
+
},
|
|
137
|
+
[SemanticType.UpdatedTimestamp]: {
|
|
138
|
+
id: SemanticType.UpdatedTimestamp,
|
|
139
|
+
displayName: 'Update Timestamp',
|
|
140
|
+
scope: SemanticScope.Property,
|
|
141
|
+
description: "Marks a field as the field that contains object's last modification timestamp.",
|
|
142
|
+
applicableDataTypes: ['datetime'],
|
|
143
|
+
},
|
|
144
|
+
[SemanticType.DeletedTimestamp]: {
|
|
145
|
+
id: SemanticType.DeletedTimestamp,
|
|
146
|
+
displayName: 'Soft Delete Timestamp',
|
|
147
|
+
scope: SemanticScope.Property,
|
|
148
|
+
description: "Marks a field as the field that contains object's deletion timestamp.",
|
|
149
|
+
applicableDataTypes: ['datetime'],
|
|
150
|
+
},
|
|
151
|
+
[SemanticType.DeletedFlag]: {
|
|
152
|
+
id: SemanticType.DeletedFlag,
|
|
153
|
+
displayName: 'Soft Delete Flag',
|
|
154
|
+
scope: SemanticScope.Property,
|
|
155
|
+
description: 'A boolean property that marks the object as deleted without physically removing it.',
|
|
156
|
+
applicableDataTypes: ['boolean'],
|
|
157
|
+
},
|
|
158
|
+
[SemanticType.ResourceOwnerIdentifier]: {
|
|
159
|
+
id: SemanticType.ResourceOwnerIdentifier,
|
|
160
|
+
displayName: 'Resource Owner Identifier',
|
|
161
|
+
scope: SemanticScope.Association,
|
|
162
|
+
description: 'Links a resource to a "User" entity instance, indicating ownership for access control.',
|
|
163
|
+
},
|
|
164
|
+
[SemanticType.PublicUniqueName]: {
|
|
165
|
+
id: SemanticType.PublicUniqueName,
|
|
166
|
+
displayName: 'Public Unique Name (Slug)',
|
|
167
|
+
scope: SemanticScope.Property,
|
|
168
|
+
description: 'A user-friendly, unique public identifier for a resource, often used in URLs.',
|
|
169
|
+
applicableDataTypes: ['string'],
|
|
170
|
+
},
|
|
171
|
+
[SemanticType.UserRole]: {
|
|
172
|
+
id: SemanticType.UserRole,
|
|
173
|
+
displayName: 'User Role Field',
|
|
174
|
+
scope: SemanticScope.Property,
|
|
175
|
+
description:
|
|
176
|
+
'A text field that is recognized by the runtime as a role of a user in the API. Used with the role-based authorization strategy.',
|
|
177
|
+
applicableDataTypes: ['string'],
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Represents the application of a data semantic to a specific
|
|
183
|
+
* entity or property within a user's data model.
|
|
184
|
+
*/
|
|
185
|
+
export interface AppliedDataSemantic {
|
|
186
|
+
/**
|
|
187
|
+
* The unique identifier of the semantic being applied.
|
|
188
|
+
*/
|
|
189
|
+
id: SemanticType
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Optional configuration or values specific to this application.
|
|
193
|
+
*/
|
|
194
|
+
config?: Record<string, unknown>
|
|
195
|
+
}
|
package/src/modeling/graph.md
CHANGED
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
```plain
|
|
6
6
|
DataDomain (Root container)
|
|
7
|
-
├──
|
|
8
|
-
│ └──
|
|
9
|
-
│ └──
|
|
7
|
+
├── DomainNamespace (logical grouping)
|
|
8
|
+
│ └── DomainModel
|
|
9
|
+
│ └── DomainEntity
|
|
10
10
|
│ ├── Property (name, type, ...)
|
|
11
11
|
│ ├── Association (name, cardinality, target Entity)
|
|
12
|
-
│ └── ParentEntity (reference to another
|
|
13
|
-
├──
|
|
14
|
-
│ └──
|
|
12
|
+
│ └── ParentEntity (reference to another DomainEntity)
|
|
13
|
+
├── DomainModel
|
|
14
|
+
│ └── DomainEntity
|
|
15
15
|
│ ├── Property
|
|
16
16
|
│ ├── Association
|
|
17
17
|
│ └── ParentEntity
|
|
18
|
-
└──
|
|
19
|
-
└──
|
|
20
|
-
└──
|
|
18
|
+
└── ForeignDomainNamespace (immutable, imported from another DataDomain)
|
|
19
|
+
└── DomainModel
|
|
20
|
+
└── DomainEntity
|
|
21
21
|
├── Property
|
|
22
22
|
├── Association
|
|
23
23
|
└── ParentEntity
|
|
@@ -31,11 +31,11 @@ Here's a breakdown of how each element is represented in the graph:
|
|
|
31
31
|
|
|
32
32
|
**Nodes:**
|
|
33
33
|
|
|
34
|
-
* **Namespace:** Represents a `
|
|
35
|
-
* **Model:** Represents a `
|
|
36
|
-
* **Entity:** Represents a `
|
|
37
|
-
* **Property:** Represents a `
|
|
38
|
-
* **Association:** Represents a `
|
|
34
|
+
* **Namespace:** Represents a `DomainNamespace`. It's a container for models and other namespaces.
|
|
35
|
+
* **Model:** Represents a `DomainModel`. It's a container for entities.
|
|
36
|
+
* **Entity:** Represents a `DomainEntity`. It's the fundamental building block, defining a specific type of data.
|
|
37
|
+
* **Property:** Represents a `DomainProperty`. It's an attribute of an entity.
|
|
38
|
+
* **Association:** Represents a `DomainAssociation`. It defines a relationship between entities.
|
|
39
39
|
|
|
40
40
|
**Edges:**
|
|
41
41
|
|
package/src/modeling/readme.md
CHANGED
|
@@ -4,53 +4,53 @@
|
|
|
4
4
|
|
|
5
5
|
At the heart of this data modeling system are the following fundamental concepts.
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### DomainNamespace
|
|
8
8
|
|
|
9
9
|
This is the top-level container, representing a logical grouping of data. Think of it as a "domain" or a "schema" in a database. It can contain:
|
|
10
10
|
|
|
11
|
-
- `
|
|
11
|
+
- `DomainModels`: Logical groupings of entities.
|
|
12
12
|
- `DataEntities`: The basic building blocks, representing individual data structures.
|
|
13
13
|
- `DataProperties`: Attributes of entities (e.g., name, age, address).
|
|
14
|
-
- `
|
|
14
|
+
- `DomainAssociations`: Relationships between entities (e.g., a user has an address).
|
|
15
15
|
- `Sub-namespaces`: Namespaces can be nested within each other, creating a hierarchical structure.
|
|
16
16
|
- `Foreign Namespaces`: References to external namespaces, enabling the use of entities defined elsewhere.
|
|
17
17
|
- `Tags`: Common tags for the entire namespace.
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### DomainModel
|
|
20
20
|
|
|
21
|
-
A logical grouping of `
|
|
21
|
+
A logical grouping of `DomainEntity` instances. It represents a specific data structure, like a "Product" or "User" model. A `DomainModel` can contain multiple `DomainEntity` instances.
|
|
22
22
|
|
|
23
|
-
###
|
|
23
|
+
### DomainEntity
|
|
24
24
|
|
|
25
25
|
The fundamental building block of the data model. It represents a specific type of data, like a "User," "Product," or "Address."
|
|
26
26
|
|
|
27
|
-
- `Properties`:
|
|
28
|
-
- `Associations`:
|
|
27
|
+
- `Properties`: DomainProperty instances that describe the attributes of the entity.
|
|
28
|
+
- `Associations`: DomainAssociation instances that define relationships to other entities.
|
|
29
29
|
- `Parents`: An entity can inherit from other entities, creating a hierarchy.
|
|
30
30
|
- `Fields`: Ordered list of properties and associations.
|
|
31
31
|
- `Tags`: Optional tags for the UI.
|
|
32
|
-
- `
|
|
32
|
+
- `Semantics`: A system of predefined, meaningful labels or classifications that can be applied to Data Entities or their individual properties.
|
|
33
33
|
- `Deprecated`: Whether the entity is deprecated.
|
|
34
34
|
- `Schema`: The schema allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
|
|
35
35
|
|
|
36
|
-
###
|
|
36
|
+
### DomainProperty
|
|
37
37
|
|
|
38
|
-
Represents an attribute of a `
|
|
38
|
+
Represents an attribute of a `DomainEntity`. It has a name, a data type (e.g., string, number, boolean), and optional constraints (e.g., required, multiple, min/max values).
|
|
39
39
|
|
|
40
40
|
- `Type`: The data type of the property.
|
|
41
41
|
- `Schema`: The general schema definition of this property.
|
|
42
42
|
- `Bindings`: The list of bindings for this property.
|
|
43
43
|
- `Tags`: Optional tags for the UI.
|
|
44
|
-
- `
|
|
44
|
+
- `Semantics`: A system of predefined, meaningful labels or classifications that can be applied to Data Entities or their individual properties.
|
|
45
45
|
- `Deprecated`: Whether the property is deprecated.
|
|
46
46
|
- `Primary`: Whether this property describes a primary key of the entity.
|
|
47
47
|
- `Index`: Whether this property describes an indexed property of the entity.
|
|
48
48
|
- `ReadOnly`: Whether the property is read only in the schema.
|
|
49
49
|
- `WriteOnly`: Whether the property is write only in the schema.
|
|
50
50
|
|
|
51
|
-
###
|
|
51
|
+
### DomainAssociation
|
|
52
52
|
|
|
53
|
-
Defines a relationship between `
|
|
53
|
+
Defines a relationship between `DomainEntity` instances. It specifies the target entities and the nature of the relationship (e.g., one-to-one, one-to-many).
|
|
54
54
|
|
|
55
55
|
- `Targets`: The list of target entities.
|
|
56
56
|
- `Multiple`: Whether the association allows multiple target entities.
|
|
@@ -67,24 +67,24 @@ Defines a translation from a data model to a specific format (like JSON, RAML, X
|
|
|
67
67
|
|
|
68
68
|
Here's how these components work together to structure a data domain:
|
|
69
69
|
|
|
70
|
-
1. **Root Namespace**: You start with a `
|
|
70
|
+
1. **Root Namespace**: You start with a `DomainNamespace`, which acts as the root of your data domain. This namespace is the top-level container for all other elements.
|
|
71
71
|
|
|
72
72
|
1. **Sub-namespaces (Optional)**: You can create sub-namespaces within the root namespace to further organize your data. This is useful for large domains with many entities and relationships.
|
|
73
73
|
|
|
74
|
-
1. **Data Models**: Within a namespace (or sub-namespace), you define `
|
|
74
|
+
1. **Data Models**: Within a namespace (or sub-namespace), you define `DomainModel` instances. Each `DomainModel` represents a specific area of your domain. For example, you might have a "User Management" `DomainModel`, a "Product Catalog" `DomainModel`, and an "Order Processing" `DomainModel`.
|
|
75
75
|
|
|
76
|
-
1. **Data Entities**: Inside each `
|
|
76
|
+
1. **Data Entities**: Inside each `DomainModel`, you define `DomainEntity` instances. These are the core data structures. For example, in the "User Management" `DomainModel`, you might have `DomainEntity` instances for "User," "Role," and "Permission."
|
|
77
77
|
|
|
78
|
-
1. **Data Properties**: Each `
|
|
78
|
+
1. **Data Properties**: Each `DomainEntity` has `DomainProperty` instances that describe its attributes. For example, the "User" `DomainEntity` might have `DomainProperty` instances for "firstName" (string), "lastName" (string), "email" (string), "age" (number), etc.
|
|
79
79
|
|
|
80
|
-
1. **Data Associations**: You use `
|
|
80
|
+
1. **Data Associations**: You use `DomainAssociation` instances to define relationships between `DomainEntity` instances. For example:
|
|
81
81
|
|
|
82
82
|
- A "User" has an "Address" (one-to-one).
|
|
83
83
|
- A "User" has multiple "Roles" (one-to-many).
|
|
84
84
|
- A "Product" belongs to a "Category" (many-to-one).
|
|
85
85
|
- An "Order" contains multiple "Order Items" (one-to-many).
|
|
86
86
|
|
|
87
|
-
1. **Inheritance**: `
|
|
87
|
+
1. **Inheritance**: `DomainEntity` instances can inherit from other `DomainEntity` instances using the parents property. This allows you to create a hierarchy of entities and reuse common properties and associations.
|
|
88
88
|
|
|
89
89
|
1. **Foreign Namespaces**: You can reference entities from other namespaces using the foreign property of the root namespace. This allows you to reuse data structures defined elsewhere.
|
|
90
90
|
|
|
@@ -109,18 +109,18 @@ Let's imagine a simple e-commerce domain:
|
|
|
109
109
|
- **Data Association**: `address` (references `Address` entity)
|
|
110
110
|
- **Data Entity**: `Address`
|
|
111
111
|
- **Data Properties**: `street` (string), `city` (string), `zipCode` (string), `country` (string)
|
|
112
|
-
- **Foreign Namespace**: `
|
|
113
|
-
- **Data Model**: `
|
|
114
|
-
- **Data Entity**: `
|
|
112
|
+
- **Foreign Namespace**: `Semantics` (defined elsewhere)
|
|
113
|
+
- **Data Model**: `Semantics`
|
|
114
|
+
- **Data Entity**: `SemanticsItem`
|
|
115
115
|
|
|
116
116
|
### Key Relationships
|
|
117
117
|
|
|
118
|
-
- **Namespace-Model**: A `
|
|
119
|
-
- **Model-Entity**: A `
|
|
120
|
-
- **Entity-Property**: A `
|
|
121
|
-
- **Entity-Association**: A `
|
|
122
|
-
- **Entity-Parent**: A `
|
|
123
|
-
- **Namespace-Foreign**: A `
|
|
118
|
+
- **Namespace-Model**: A `DomainNamespace` contains `DomainModel` instances.
|
|
119
|
+
- **Model-Entity**: A `DomainModel` contains `DomainEntity` instances.
|
|
120
|
+
- **Entity-Property**: A `DomainEntity` has `DomainProperty` instances.
|
|
121
|
+
- **Entity-Association**: A `DomainEntity` has `DomainAssociation` instances.
|
|
122
|
+
- **Entity-Parent**: A `DomainEntity` can have `DomainEntity` instances as parents.
|
|
123
|
+
- **Namespace-Foreign**: A `DomainNamespace` can have references to `DomainNamespace` instances.
|
|
124
124
|
|
|
125
125
|
## Summary
|
|
126
126
|
|
package/src/models/kinds.ts
CHANGED
|
@@ -20,28 +20,3 @@ export const DataCatalogKind = 'Core#DataCatalog'
|
|
|
20
20
|
export const DataCatalogVersionKind = 'Core#DataCatalogVersion'
|
|
21
21
|
export const OrganizationKind = 'Core#Organization'
|
|
22
22
|
export const InvitationKind = 'Core#Invitation'
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated Not used anymore.
|
|
26
|
-
*/
|
|
27
|
-
export const DataFileKind = 'Core#DataFile'
|
|
28
|
-
/**
|
|
29
|
-
* @deprecated Not used anymore.
|
|
30
|
-
*/
|
|
31
|
-
export const DataNamespaceKind = 'Data#DataNamespace'
|
|
32
|
-
/**
|
|
33
|
-
* @deprecated Not used anymore.
|
|
34
|
-
*/
|
|
35
|
-
export const DataModelKind = 'Data#DataModel'
|
|
36
|
-
/**
|
|
37
|
-
* @deprecated Not used anymore.
|
|
38
|
-
*/
|
|
39
|
-
export const DataEntityKind = 'Data#DataEntity'
|
|
40
|
-
/**
|
|
41
|
-
* @deprecated Not used anymore.
|
|
42
|
-
*/
|
|
43
|
-
export const DataAssociationKind = 'Data#DataAssociation'
|
|
44
|
-
/**
|
|
45
|
-
* @deprecated Not used anymore.
|
|
46
|
-
*/
|
|
47
|
-
export const DataPropertyKind = 'Data#DataProperty'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { DataDomain, DataFormat } from '../../../src/index.js'
|
|
3
|
+
import { SemanticType } from '../../../src/modeling/Semantics.js'
|
|
3
4
|
|
|
4
5
|
test.group('DataDomain change observers', () => {
|
|
5
6
|
test('does not notifies a change when initializing a domain', async ({ assert }) => {
|
|
@@ -276,24 +277,24 @@ test.group('DomainEntity change observers', () => {
|
|
|
276
277
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
277
278
|
})
|
|
278
279
|
|
|
279
|
-
test('notifies a change when adding a
|
|
280
|
+
test('notifies a change when adding a semantics', async ({ assert, sleep }) => {
|
|
280
281
|
const domain = new DataDomain()
|
|
281
282
|
const n1 = domain.addNamespace()
|
|
282
283
|
const m1 = n1.addModel()
|
|
283
284
|
const e1 = m1.addEntity()
|
|
284
285
|
await sleep(1)
|
|
285
|
-
e1.
|
|
286
|
+
e1.semantics.push({ id: SemanticType.User })
|
|
286
287
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
287
288
|
})
|
|
288
289
|
|
|
289
|
-
test('notifies a change when removing a
|
|
290
|
+
test('notifies a change when removing a semantics', async ({ assert, sleep }) => {
|
|
290
291
|
const domain = new DataDomain()
|
|
291
292
|
const n1 = domain.addNamespace()
|
|
292
293
|
const m1 = n1.addModel()
|
|
293
294
|
const e1 = m1.addEntity()
|
|
294
|
-
e1.
|
|
295
|
+
e1.semantics.push({ id: SemanticType.User })
|
|
295
296
|
await sleep(1)
|
|
296
|
-
e1.
|
|
297
|
+
e1.semantics.pop()
|
|
297
298
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
298
299
|
})
|
|
299
300
|
|
|
@@ -508,26 +509,26 @@ test.group('DomainProperty change observers', () => {
|
|
|
508
509
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
509
510
|
})
|
|
510
511
|
|
|
511
|
-
test('notifies a change when adding a
|
|
512
|
+
test('notifies a change when adding a semantics', async ({ assert, sleep }) => {
|
|
512
513
|
const domain = new DataDomain()
|
|
513
514
|
const n1 = domain.addNamespace()
|
|
514
515
|
const m1 = n1.addModel()
|
|
515
516
|
const e1 = m1.addEntity()
|
|
516
517
|
const p1 = e1.addProperty()
|
|
517
518
|
await sleep(1)
|
|
518
|
-
p1.
|
|
519
|
+
p1.semantics.push({ id: SemanticType.CreatedTimestamp })
|
|
519
520
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
520
521
|
})
|
|
521
522
|
|
|
522
|
-
test('notifies a change when removing a
|
|
523
|
+
test('notifies a change when removing a semantics', async ({ assert, sleep }) => {
|
|
523
524
|
const domain = new DataDomain()
|
|
524
525
|
const n1 = domain.addNamespace()
|
|
525
526
|
const m1 = n1.addModel()
|
|
526
527
|
const e1 = m1.addEntity()
|
|
527
528
|
const p1 = e1.addProperty()
|
|
528
|
-
p1.
|
|
529
|
+
p1.semantics.push({ id: SemanticType.CreatedTimestamp })
|
|
529
530
|
await sleep(1)
|
|
530
|
-
p1.
|
|
531
|
+
p1.semantics.pop()
|
|
531
532
|
await assert.dispatchCount(domain, 'change', 1, { timeout: 1 })
|
|
532
533
|
})
|
|
533
534
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { test } from '@japa/runner'
|
|
2
2
|
import { DataDomain } from '../../../src/modeling/DataDomain.js'
|
|
3
3
|
import { DomainEntity } from '../../../src/modeling/DomainEntity.js'
|
|
4
|
+
import { SemanticType } from '../../../src/modeling/Semantics.js'
|
|
4
5
|
|
|
5
6
|
test.group('DataDomain.addEntity()', () => {
|
|
6
7
|
test('addEntity adds an entity to the graph', ({ assert }) => {
|
|
@@ -70,7 +71,7 @@ test.group('DataDomain.addEntity()', () => {
|
|
|
70
71
|
assert.throws(() => {
|
|
71
72
|
// @ts-expect-error For testing purposes, we are passing undefined
|
|
72
73
|
dataDomain.addEntity(undefined, { key: 'test-entity' })
|
|
73
|
-
}, `An entity expects a
|
|
74
|
+
}, `An entity expects a DomainModel parent`)
|
|
74
75
|
})
|
|
75
76
|
|
|
76
77
|
test('addEntity throws when adding the same entity twice', ({ assert }) => {
|
|
@@ -328,6 +329,133 @@ test.group('DataDomain.findEntity()', () => {
|
|
|
328
329
|
})
|
|
329
330
|
})
|
|
330
331
|
|
|
332
|
+
test.group('DomainEntity.addSemantic()', () => {
|
|
333
|
+
test('adds a new semantic to the entity', ({ assert }) => {
|
|
334
|
+
const dataDomain = new DataDomain()
|
|
335
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
336
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
337
|
+
const semantic = { id: SemanticType.User }
|
|
338
|
+
entity.addSemantic(semantic)
|
|
339
|
+
assert.deepInclude(entity.semantics, semantic)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
test('updates an existing semantic', ({ assert }) => {
|
|
343
|
+
const dataDomain = new DataDomain()
|
|
344
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
345
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
346
|
+
const semantic1 = { id: SemanticType.User, config: { value: 'old' } }
|
|
347
|
+
const semantic2 = { id: SemanticType.User, config: { value: 'new' } }
|
|
348
|
+
entity.addSemantic(semantic1)
|
|
349
|
+
entity.addSemantic(semantic2)
|
|
350
|
+
assert.lengthOf(entity.semantics, 1)
|
|
351
|
+
assert.deepInclude(entity.semantics, semantic2)
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
test('throws an error if the semantic is not an entity semantic', ({ assert }) => {
|
|
355
|
+
const dataDomain = new DataDomain()
|
|
356
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
357
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
358
|
+
const semantic = { id: SemanticType.CreatedTimestamp } // CreatedTimestamp is a Property semantic
|
|
359
|
+
assert.throws(() => {
|
|
360
|
+
entity.addSemantic(semantic)
|
|
361
|
+
}, `Invalid semantic type: ${SemanticType.CreatedTimestamp}. Expected an entity semantic.`)
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
test('notifies change when adding a new semantic', async ({ assert }) => {
|
|
365
|
+
const dataDomain = new DataDomain()
|
|
366
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
367
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
368
|
+
const semantic = { id: SemanticType.User }
|
|
369
|
+
entity.addSemantic(semantic)
|
|
370
|
+
await assert.dispatches(dataDomain, 'change', { timeout: 20 })
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
test('notifies change when updating an existing semantic', async ({ assert }) => {
|
|
374
|
+
const dataDomain = new DataDomain()
|
|
375
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
376
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
377
|
+
const semantic1 = { id: SemanticType.User, config: { value: 'old' } }
|
|
378
|
+
const semantic2 = { id: SemanticType.User, config: { value: 'new' } }
|
|
379
|
+
entity.addSemantic(semantic1)
|
|
380
|
+
// Clear the event queue before the second add
|
|
381
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
382
|
+
entity.addSemantic(semantic2)
|
|
383
|
+
await assert.dispatches(dataDomain, 'change', { timeout: 20 })
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
test.group('DomainEntity.removeSemantic()', () => {
|
|
388
|
+
test('removes an existing semantic from the entity', ({ assert }) => {
|
|
389
|
+
const dataDomain = new DataDomain()
|
|
390
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
391
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
392
|
+
const semantic = { id: SemanticType.User }
|
|
393
|
+
entity.addSemantic(semantic)
|
|
394
|
+
assert.deepInclude(entity.semantics, semantic)
|
|
395
|
+
entity.removeSemantic(SemanticType.User)
|
|
396
|
+
assert.notDeepInclude(entity.semantics, semantic)
|
|
397
|
+
assert.lengthOf(entity.semantics, 0)
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
test('does nothing if the semantic to remove does not exist', ({ assert }) => {
|
|
401
|
+
const dataDomain = new DataDomain()
|
|
402
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
403
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
404
|
+
const semantic = { id: SemanticType.User }
|
|
405
|
+
entity.addSemantic(semantic)
|
|
406
|
+
const initialSemantics = [...entity.semantics]
|
|
407
|
+
entity.removeSemantic('non-existent-semantic-id' as SemanticType)
|
|
408
|
+
assert.deepEqual(entity.semantics, initialSemantics)
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
test('notifies change when a semantic is removed', async ({ assert }) => {
|
|
412
|
+
const dataDomain = new DataDomain()
|
|
413
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
414
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
415
|
+
const semantic = { id: SemanticType.User }
|
|
416
|
+
entity.addSemantic(semantic)
|
|
417
|
+
// Clear the event queue before removal
|
|
418
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
419
|
+
entity.removeSemantic(SemanticType.User)
|
|
420
|
+
await assert.dispatches(dataDomain, 'change', { timeout: 20 })
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
test('does not notify change if the semantic to remove does not exist', async ({ assert }) => {
|
|
424
|
+
const dataDomain = new DataDomain()
|
|
425
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
426
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
427
|
+
// Clear the event queue before removal
|
|
428
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
429
|
+
entity.removeSemantic('non-existent-semantic-id' as SemanticType)
|
|
430
|
+
await assert.notDispatches(dataDomain, 'change', { timeout: 20 })
|
|
431
|
+
})
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
test.group('DomainEntity.hasSemantic()', () => {
|
|
435
|
+
test('returns true if the entity has the specified semantic', ({ assert }) => {
|
|
436
|
+
const dataDomain = new DataDomain()
|
|
437
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
438
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
439
|
+
const semantic = { id: SemanticType.User }
|
|
440
|
+
entity.addSemantic(semantic)
|
|
441
|
+
assert.isTrue(entity.hasSemantic(SemanticType.User))
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
test('returns false if the entity does not have the specified semantic', ({ assert }) => {
|
|
445
|
+
const dataDomain = new DataDomain()
|
|
446
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
447
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
448
|
+
assert.isFalse(entity.hasSemantic(SemanticType.User))
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
test('returns false for an empty semantics array', ({ assert }) => {
|
|
452
|
+
const dataDomain = new DataDomain()
|
|
453
|
+
const model = dataDomain.addModel({ key: 'test-model' })
|
|
454
|
+
const entity = dataDomain.addEntity(model.key, { key: 'test-entity' })
|
|
455
|
+
assert.isFalse(entity.hasSemantic(SemanticType.User))
|
|
456
|
+
})
|
|
457
|
+
})
|
|
458
|
+
|
|
331
459
|
test.group('DataDomain.moveEntity()', () => {
|
|
332
460
|
test('moves an entity from one model to another', ({ assert }) => {
|
|
333
461
|
const dataDomain = new DataDomain()
|
|
@@ -67,7 +67,7 @@ test.group('DataDomain.addProperty()', () => {
|
|
|
67
67
|
assert.throws(() => {
|
|
68
68
|
// @ts-expect-error For testing purposes, we are passing undefined
|
|
69
69
|
dataDomain.addProperty(undefined)
|
|
70
|
-
}, 'A property expects a
|
|
70
|
+
}, 'A property expects a DomainEntity parent')
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
test('addProperty throws when adding property to a foreign entity', ({ assert }) => {
|