@api-client/core 0.11.2 → 0.11.4
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/Local.session.sql +0 -0
- package/build/src/modeling/DataAssociation.d.ts +3 -9
- package/build/src/modeling/DataAssociation.d.ts.map +1 -1
- package/build/src/modeling/DataAssociation.js +2 -0
- package/build/src/modeling/DataAssociation.js.map +1 -1
- package/build/src/modeling/DataModel.d.ts +10 -1
- package/build/src/modeling/DataModel.d.ts.map +1 -1
- package/build/src/modeling/DataModel.js +39 -0
- package/build/src/modeling/DataModel.js.map +1 -1
- package/build/src/modeling/DataNamespace.d.ts +31 -1
- package/build/src/modeling/DataNamespace.d.ts.map +1 -1
- package/build/src/modeling/DataNamespace.js +79 -0
- package/build/src/modeling/DataNamespace.js.map +1 -1
- package/data/models/example-generator-api.json +26 -26
- package/data/models/multiple-servers.json +16 -0
- package/data/models/petstore.json +92 -7
- package/package.json +1 -1
- package/src/modeling/DataAssociation.ts +2 -15
- package/src/modeling/DataModel.ts +40 -1
- package/src/modeling/DataNamespace.ts +88 -1
- package/src/modeling/readme.md +134 -0
- package/tests/unit/modeling/data_model.spec.ts +54 -0
- package/tests/unit/modeling/data_namespace.spec.ts +383 -1
- package/build/oauth-popup.html +0 -33
|
@@ -4447,6 +4447,11 @@
|
|
|
4447
4447
|
{
|
|
4448
4448
|
"@id": "#547"
|
|
4449
4449
|
}
|
|
4450
|
+
],
|
|
4451
|
+
"sourcemaps:sources": [
|
|
4452
|
+
{
|
|
4453
|
+
"@id": "#546/source-map"
|
|
4454
|
+
}
|
|
4450
4455
|
]
|
|
4451
4456
|
},
|
|
4452
4457
|
{
|
|
@@ -5259,6 +5264,9 @@
|
|
|
5259
5264
|
],
|
|
5260
5265
|
"sourcemaps:lexical": [
|
|
5261
5266
|
{
|
|
5267
|
+
"@id": "#295/source-map/lexical/element_1"
|
|
5268
|
+
},
|
|
5269
|
+
{
|
|
5262
5270
|
"@id": "#295/source-map/lexical/element_0"
|
|
5263
5271
|
}
|
|
5264
5272
|
]
|
|
@@ -5442,6 +5450,9 @@
|
|
|
5442
5450
|
],
|
|
5443
5451
|
"sourcemaps:lexical": [
|
|
5444
5452
|
{
|
|
5453
|
+
"@id": "#319/source-map/lexical/element_1"
|
|
5454
|
+
},
|
|
5455
|
+
{
|
|
5445
5456
|
"@id": "#319/source-map/lexical/element_0"
|
|
5446
5457
|
}
|
|
5447
5458
|
]
|
|
@@ -5623,6 +5634,9 @@
|
|
|
5623
5634
|
],
|
|
5624
5635
|
"sourcemaps:lexical": [
|
|
5625
5636
|
{
|
|
5637
|
+
"@id": "#337/source-map/lexical/element_1"
|
|
5638
|
+
},
|
|
5639
|
+
{
|
|
5626
5640
|
"@id": "#337/source-map/lexical/element_0"
|
|
5627
5641
|
}
|
|
5628
5642
|
]
|
|
@@ -5948,13 +5962,16 @@
|
|
|
5948
5962
|
],
|
|
5949
5963
|
"sourcemaps:lexical": [
|
|
5950
5964
|
{
|
|
5951
|
-
"@id": "#351/source-map/lexical/
|
|
5965
|
+
"@id": "#351/source-map/lexical/element_3"
|
|
5966
|
+
},
|
|
5967
|
+
{
|
|
5968
|
+
"@id": "#351/source-map/lexical/element_1"
|
|
5952
5969
|
},
|
|
5953
5970
|
{
|
|
5954
5971
|
"@id": "#351/source-map/lexical/element_0"
|
|
5955
5972
|
},
|
|
5956
5973
|
{
|
|
5957
|
-
"@id": "#351/source-map/lexical/
|
|
5974
|
+
"@id": "#351/source-map/lexical/element_2"
|
|
5958
5975
|
}
|
|
5959
5976
|
]
|
|
5960
5977
|
},
|
|
@@ -6104,10 +6121,13 @@
|
|
|
6104
6121
|
],
|
|
6105
6122
|
"sourcemaps:lexical": [
|
|
6106
6123
|
{
|
|
6107
|
-
"@id": "#378/source-map/lexical/
|
|
6124
|
+
"@id": "#378/source-map/lexical/element_2"
|
|
6108
6125
|
},
|
|
6109
6126
|
{
|
|
6110
6127
|
"@id": "#378/source-map/lexical/element_0"
|
|
6128
|
+
},
|
|
6129
|
+
{
|
|
6130
|
+
"@id": "#378/source-map/lexical/element_1"
|
|
6111
6131
|
}
|
|
6112
6132
|
]
|
|
6113
6133
|
},
|
|
@@ -6921,6 +6941,9 @@
|
|
|
6921
6941
|
],
|
|
6922
6942
|
"sourcemaps:lexical": [
|
|
6923
6943
|
{
|
|
6944
|
+
"@id": "#456/source-map/lexical/element_1"
|
|
6945
|
+
},
|
|
6946
|
+
{
|
|
6924
6947
|
"@id": "#456/source-map/lexical/element_0"
|
|
6925
6948
|
}
|
|
6926
6949
|
]
|
|
@@ -7108,6 +7131,9 @@
|
|
|
7108
7131
|
],
|
|
7109
7132
|
"sourcemaps:lexical": [
|
|
7110
7133
|
{
|
|
7134
|
+
"@id": "#466/source-map/lexical/element_1"
|
|
7135
|
+
},
|
|
7136
|
+
{
|
|
7111
7137
|
"@id": "#466/source-map/lexical/element_0"
|
|
7112
7138
|
}
|
|
7113
7139
|
]
|
|
@@ -7655,6 +7681,9 @@
|
|
|
7655
7681
|
],
|
|
7656
7682
|
"sourcemaps:lexical": [
|
|
7657
7683
|
{
|
|
7684
|
+
"@id": "#509/source-map/lexical/element_1"
|
|
7685
|
+
},
|
|
7686
|
+
{
|
|
7658
7687
|
"@id": "#509/source-map/lexical/element_0"
|
|
7659
7688
|
}
|
|
7660
7689
|
]
|
|
@@ -8243,6 +8272,17 @@
|
|
|
8243
8272
|
]
|
|
8244
8273
|
},
|
|
8245
8274
|
{
|
|
8275
|
+
"@id": "#546/source-map",
|
|
8276
|
+
"@type": [
|
|
8277
|
+
"sourcemaps:SourceMap"
|
|
8278
|
+
],
|
|
8279
|
+
"sourcemaps:lexical": [
|
|
8280
|
+
{
|
|
8281
|
+
"@id": "#546/source-map/lexical/element_0"
|
|
8282
|
+
}
|
|
8283
|
+
]
|
|
8284
|
+
},
|
|
8285
|
+
{
|
|
8246
8286
|
"@id": "#544/source-map",
|
|
8247
8287
|
"@type": [
|
|
8248
8288
|
"sourcemaps:SourceMap"
|
|
@@ -9027,6 +9067,11 @@
|
|
|
9027
9067
|
]
|
|
9028
9068
|
},
|
|
9029
9069
|
{
|
|
9070
|
+
"@id": "#295/source-map/lexical/element_1",
|
|
9071
|
+
"sourcemaps:element": "amf://id#295",
|
|
9072
|
+
"sourcemaps:value": "[(121,6)-(135,0)]"
|
|
9073
|
+
},
|
|
9074
|
+
{
|
|
9030
9075
|
"@id": "#295/source-map/lexical/element_0",
|
|
9031
9076
|
"sourcemaps:element": "apiContract:parameter",
|
|
9032
9077
|
"sourcemaps:value": "[(121,6)-(135,0)]"
|
|
@@ -9234,6 +9279,11 @@
|
|
|
9234
9279
|
]
|
|
9235
9280
|
},
|
|
9236
9281
|
{
|
|
9282
|
+
"@id": "#319/source-map/lexical/element_1",
|
|
9283
|
+
"sourcemaps:element": "amf://id#319",
|
|
9284
|
+
"sourcemaps:value": "[(164,6)-(174,0)]"
|
|
9285
|
+
},
|
|
9286
|
+
{
|
|
9237
9287
|
"@id": "#319/source-map/lexical/element_0",
|
|
9238
9288
|
"sourcemaps:element": "apiContract:parameter",
|
|
9239
9289
|
"sourcemaps:value": "[(164,6)-(174,0)]"
|
|
@@ -9444,6 +9494,11 @@
|
|
|
9444
9494
|
]
|
|
9445
9495
|
},
|
|
9446
9496
|
{
|
|
9497
|
+
"@id": "#337/source-map/lexical/element_1",
|
|
9498
|
+
"sourcemaps:element": "amf://id#337",
|
|
9499
|
+
"sourcemaps:value": "[(201,6)-(209,0)]"
|
|
9500
|
+
},
|
|
9501
|
+
{
|
|
9447
9502
|
"@id": "#337/source-map/lexical/element_0",
|
|
9448
9503
|
"sourcemaps:element": "apiContract:uriParameter",
|
|
9449
9504
|
"sourcemaps:value": "[(201,6)-(209,0)]"
|
|
@@ -9901,18 +9956,23 @@
|
|
|
9901
9956
|
]
|
|
9902
9957
|
},
|
|
9903
9958
|
{
|
|
9904
|
-
"@id": "#351/source-map/lexical/
|
|
9959
|
+
"@id": "#351/source-map/lexical/element_3",
|
|
9905
9960
|
"sourcemaps:element": "apiContract:uriParameter",
|
|
9906
9961
|
"sourcemaps:value": "[(234,6)-(275,0)]"
|
|
9907
9962
|
},
|
|
9908
9963
|
{
|
|
9964
|
+
"@id": "#351/source-map/lexical/element_1",
|
|
9965
|
+
"sourcemaps:element": "apiContract:parameter",
|
|
9966
|
+
"sourcemaps:value": "[(234,6)-(275,0)]"
|
|
9967
|
+
},
|
|
9968
|
+
{
|
|
9909
9969
|
"@id": "#351/source-map/lexical/element_0",
|
|
9910
9970
|
"sourcemaps:element": "apiContract:cookieParameter",
|
|
9911
9971
|
"sourcemaps:value": "[(234,6)-(275,0)]"
|
|
9912
9972
|
},
|
|
9913
9973
|
{
|
|
9914
|
-
"@id": "#351/source-map/lexical/
|
|
9915
|
-
"sourcemaps:element": "
|
|
9974
|
+
"@id": "#351/source-map/lexical/element_2",
|
|
9975
|
+
"sourcemaps:element": "amf://id#351",
|
|
9916
9976
|
"sourcemaps:value": "[(234,6)-(275,0)]"
|
|
9917
9977
|
},
|
|
9918
9978
|
{
|
|
@@ -10087,7 +10147,7 @@
|
|
|
10087
10147
|
]
|
|
10088
10148
|
},
|
|
10089
10149
|
{
|
|
10090
|
-
"@id": "#378/source-map/lexical/
|
|
10150
|
+
"@id": "#378/source-map/lexical/element_2",
|
|
10091
10151
|
"sourcemaps:element": "apiContract:uriParameter",
|
|
10092
10152
|
"sourcemaps:value": "[(288,6)-(302,0)]"
|
|
10093
10153
|
},
|
|
@@ -10097,6 +10157,11 @@
|
|
|
10097
10157
|
"sourcemaps:value": "[(288,6)-(302,0)]"
|
|
10098
10158
|
},
|
|
10099
10159
|
{
|
|
10160
|
+
"@id": "#378/source-map/lexical/element_1",
|
|
10161
|
+
"sourcemaps:element": "amf://id#378",
|
|
10162
|
+
"sourcemaps:value": "[(288,6)-(302,0)]"
|
|
10163
|
+
},
|
|
10164
|
+
{
|
|
10100
10165
|
"@id": "#377/source-map/lexical/element_2",
|
|
10101
10166
|
"sourcemaps:element": "core:name",
|
|
10102
10167
|
"sourcemaps:value": "[(303,8)-(303,13)]"
|
|
@@ -10901,6 +10966,11 @@
|
|
|
10901
10966
|
]
|
|
10902
10967
|
},
|
|
10903
10968
|
{
|
|
10969
|
+
"@id": "#456/source-map/lexical/element_1",
|
|
10970
|
+
"sourcemaps:element": "amf://id#456",
|
|
10971
|
+
"sourcemaps:value": "[(443,6)-(451,0)]"
|
|
10972
|
+
},
|
|
10973
|
+
{
|
|
10904
10974
|
"@id": "#456/source-map/lexical/element_0",
|
|
10905
10975
|
"sourcemaps:element": "apiContract:uriParameter",
|
|
10906
10976
|
"sourcemaps:value": "[(443,6)-(451,0)]"
|
|
@@ -11069,6 +11139,11 @@
|
|
|
11069
11139
|
]
|
|
11070
11140
|
},
|
|
11071
11141
|
{
|
|
11142
|
+
"@id": "#466/source-map/lexical/element_1",
|
|
11143
|
+
"sourcemaps:element": "amf://id#466",
|
|
11144
|
+
"sourcemaps:value": "[(474,6)-(482,0)]"
|
|
11145
|
+
},
|
|
11146
|
+
{
|
|
11072
11147
|
"@id": "#466/source-map/lexical/element_0",
|
|
11073
11148
|
"sourcemaps:element": "apiContract:uriParameter",
|
|
11074
11149
|
"sourcemaps:value": "[(474,6)-(482,0)]"
|
|
@@ -11641,6 +11716,11 @@
|
|
|
11641
11716
|
]
|
|
11642
11717
|
},
|
|
11643
11718
|
{
|
|
11719
|
+
"@id": "#509/source-map/lexical/element_1",
|
|
11720
|
+
"sourcemaps:element": "amf://id#509",
|
|
11721
|
+
"sourcemaps:value": "[(564,6)-(577,0)]"
|
|
11722
|
+
},
|
|
11723
|
+
{
|
|
11644
11724
|
"@id": "#509/source-map/lexical/element_0",
|
|
11645
11725
|
"sourcemaps:element": "apiContract:parameter",
|
|
11646
11726
|
"sourcemaps:value": "[(564,6)-(577,0)]"
|
|
@@ -12256,6 +12336,11 @@
|
|
|
12256
12336
|
]
|
|
12257
12337
|
},
|
|
12258
12338
|
{
|
|
12339
|
+
"@id": "#546/source-map/lexical/element_0",
|
|
12340
|
+
"sourcemaps:element": "amf://id#546",
|
|
12341
|
+
"sourcemaps:value": "[(678,6)-(685,0)]"
|
|
12342
|
+
},
|
|
12343
|
+
{
|
|
12259
12344
|
"@id": "#544/source-map/lexical/element_2",
|
|
12260
12345
|
"sourcemaps:element": "core:name",
|
|
12261
12346
|
"sourcemaps:value": "[(686,8)-(686,13)]"
|
package/package.json
CHANGED
|
@@ -388,6 +388,8 @@ export class DataAssociation {
|
|
|
388
388
|
|
|
389
389
|
/**
|
|
390
390
|
* @param init The key of an entity, its instance, or schema.
|
|
391
|
+
* @param namespace - The key of the foreign namespace this entity belongs to.
|
|
392
|
+
* Do not set this value for local entities.
|
|
391
393
|
*/
|
|
392
394
|
addTarget(init: string | DataEntity | IDataEntity, namespace?: string): void {
|
|
393
395
|
let key: string
|
|
@@ -415,21 +417,6 @@ export class DataAssociation {
|
|
|
415
417
|
this.targets.push(info)
|
|
416
418
|
}
|
|
417
419
|
|
|
418
|
-
/**
|
|
419
|
-
* Removes a target entity from the association.
|
|
420
|
-
*
|
|
421
|
-
* @param entity - The `DataEntity` or `IDataEntity` instance to remove.
|
|
422
|
-
*/
|
|
423
|
-
removeTarget(entity: DataEntity | IDataEntity): void
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Removes a target entity from the association.
|
|
427
|
-
*
|
|
428
|
-
* @param key - The key of the target entity to remove.
|
|
429
|
-
*/
|
|
430
|
-
// eslint-disable-next-line @typescript-eslint/unified-signatures
|
|
431
|
-
removeTarget(key: string): void
|
|
432
|
-
|
|
433
420
|
/**
|
|
434
421
|
* Removes a target entity from the targets list.
|
|
435
422
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IThing, Thing } from '../models/Thing.js'
|
|
2
2
|
import v4 from '../lib/uuid.js'
|
|
3
3
|
import { DataEntity, IDataEntity } from './DataEntity.js'
|
|
4
|
-
import { DataNamespace } from './DataNamespace.js'
|
|
4
|
+
import type { DataItemAdaptingOptions, DataNamespace } from './DataNamespace.js'
|
|
5
5
|
import { FileBreadcrumb } from '../models/store/File.js'
|
|
6
6
|
import { DataModelKind } from '../models/kinds.js'
|
|
7
7
|
|
|
@@ -149,6 +149,45 @@ export class DataModel {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Adapts an entity to this data model.
|
|
154
|
+
* The entity must already be added to another data model within this namespace for it to be
|
|
155
|
+
* adapted to this data model.
|
|
156
|
+
* This method is used to change the data model of an entity.
|
|
157
|
+
* It will remove the entity from the previous data model and add it to this one.
|
|
158
|
+
* @param entity The entity to adapt.
|
|
159
|
+
*/
|
|
160
|
+
adaptEntity(entity: DataEntity, opts: DataItemAdaptingOptions = {}): void {
|
|
161
|
+
if (opts.index !== undefined) {
|
|
162
|
+
if (opts.index < 0) {
|
|
163
|
+
throw new Error(`The index ${opts.index} cannot be below 0.`)
|
|
164
|
+
}
|
|
165
|
+
if (opts.index >= this.entities.length) {
|
|
166
|
+
throw new Error(`The index ${opts.index} is not valid.`)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (entity.root !== this.root) {
|
|
170
|
+
throw new Error(`The entity ${entity.key} is not in the same namespace as this data model.`)
|
|
171
|
+
}
|
|
172
|
+
if (entity.getParent() === this) {
|
|
173
|
+
throw new Error(`The entity ${entity.key} is already adapted by this data model.`)
|
|
174
|
+
}
|
|
175
|
+
const parent = entity.getParent()
|
|
176
|
+
if (!parent) {
|
|
177
|
+
throw new Error(`The entity ${entity.key} has no parent.`)
|
|
178
|
+
}
|
|
179
|
+
const index = parent.entities.findIndex((i) => i.key === entity.key)
|
|
180
|
+
if (index < 0) {
|
|
181
|
+
throw new Error(`The entity ${entity.key} is not in the parent data model.`)
|
|
182
|
+
}
|
|
183
|
+
parent.entities.splice(index, 1)
|
|
184
|
+
if (opts.index !== undefined) {
|
|
185
|
+
this.entities.splice(opts.index, 0, entity)
|
|
186
|
+
} else {
|
|
187
|
+
this.entities.push(entity)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
152
191
|
/**
|
|
153
192
|
* Adds an entity to this data model.
|
|
154
193
|
*
|
|
@@ -9,6 +9,13 @@ import v4 from '../lib/uuid.js'
|
|
|
9
9
|
|
|
10
10
|
type ItemKind = typeof DataNamespaceKind | typeof DataModelKind
|
|
11
11
|
|
|
12
|
+
export interface DataItemAdaptingOptions {
|
|
13
|
+
/**
|
|
14
|
+
* The index at which to adapt the item.
|
|
15
|
+
*/
|
|
16
|
+
index?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
interface IDataDefinitions {
|
|
13
20
|
models?: IDataModel[]
|
|
14
21
|
entities?: IDataEntity[]
|
|
@@ -44,7 +51,7 @@ interface DataDefinitions {
|
|
|
44
51
|
|
|
45
52
|
/**
|
|
46
53
|
* Data definition for a foreign namespace.
|
|
47
|
-
* Each
|
|
54
|
+
* Each foreign namespace is resolved to a specific version.
|
|
48
55
|
* This makes sure that the local data are always referencing an existing
|
|
49
56
|
* entity as breaking changes should be resolved when upgrading a version.
|
|
50
57
|
*/
|
|
@@ -435,6 +442,14 @@ export class DataNamespace extends DataNamespaceParent {
|
|
|
435
442
|
return result
|
|
436
443
|
}
|
|
437
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Checks if this is the root namespace.
|
|
447
|
+
* @returns True if this is the root namespace.
|
|
448
|
+
*/
|
|
449
|
+
isRoot(): boolean {
|
|
450
|
+
return this.root === undefined
|
|
451
|
+
}
|
|
452
|
+
|
|
438
453
|
/**
|
|
439
454
|
* Finds a parent namespace for the given namespace.
|
|
440
455
|
* @param key The namespace key to find the parent for.
|
|
@@ -486,6 +501,55 @@ export class DataNamespace extends DataNamespaceParent {
|
|
|
486
501
|
return definition
|
|
487
502
|
}
|
|
488
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Adapts an existing namespace to this namespace.
|
|
506
|
+
* This will remove the namespace from the parent namespace and add it to this one.
|
|
507
|
+
*
|
|
508
|
+
* @param ns The namespace to adapt.
|
|
509
|
+
* @param opts Adapting options.
|
|
510
|
+
*/
|
|
511
|
+
adaptNamespace(ns: DataNamespace, opts: DataItemAdaptingOptions = {}): void {
|
|
512
|
+
if (opts.index !== undefined) {
|
|
513
|
+
if (opts.index < 0) {
|
|
514
|
+
throw new Error(`The index ${opts.index} cannot be below 0.`)
|
|
515
|
+
}
|
|
516
|
+
if (opts.index >= this.items.length) {
|
|
517
|
+
throw new Error(`The index ${opts.index} is not valid.`)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (ns === this) {
|
|
521
|
+
throw new Error(`Unable to adapt a namespace that is self.`)
|
|
522
|
+
}
|
|
523
|
+
if (ns.root !== this.root) {
|
|
524
|
+
throw new Error(`The namespace ${ns.key} is not in the same namespace as this data namespace.`)
|
|
525
|
+
}
|
|
526
|
+
if (ns.getParent() === this) {
|
|
527
|
+
throw new Error(`The namespace ${ns.key} is already adapted by this data namespace.`)
|
|
528
|
+
}
|
|
529
|
+
// Check for circular dependency
|
|
530
|
+
let current: DataNamespace | undefined = this
|
|
531
|
+
while (current) {
|
|
532
|
+
if (current === ns) {
|
|
533
|
+
throw new Error(`Unable to adapt namespace ${ns.key} under itself or one of its children.`)
|
|
534
|
+
}
|
|
535
|
+
current = current.getParent()
|
|
536
|
+
}
|
|
537
|
+
const parent = ns.getParent() || this.root
|
|
538
|
+
if (!parent) {
|
|
539
|
+
throw new Error(`The namespace ${ns.key} has no parent.`)
|
|
540
|
+
}
|
|
541
|
+
const index = parent.items.findIndex((i) => i.key === ns.key)
|
|
542
|
+
if (index < 0) {
|
|
543
|
+
throw new Error(`The namespace ${ns.key} is not in the parent data namespace.`)
|
|
544
|
+
}
|
|
545
|
+
const [item] = parent.items.splice(index, 1)
|
|
546
|
+
if (opts.index !== undefined) {
|
|
547
|
+
this.items.splice(opts.index, 0, item)
|
|
548
|
+
} else {
|
|
549
|
+
this.items.push(item)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
489
553
|
/**
|
|
490
554
|
* Finds a namespace in the definitions.
|
|
491
555
|
* @param key The key of the namespace to find.
|
|
@@ -715,6 +779,29 @@ export class DataNamespace extends DataNamespaceParent {
|
|
|
715
779
|
return result
|
|
716
780
|
}
|
|
717
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Finds an associated entity in the current or foreign namespace.
|
|
784
|
+
* This is a helper function to discover entities in the current and foreign namespaces.
|
|
785
|
+
*
|
|
786
|
+
* @param key The key of the entity to find.
|
|
787
|
+
* @param namespace The optional namespace to search in.
|
|
788
|
+
* If not set, the current namespace is used.
|
|
789
|
+
* This is used to find entities in foreign namespaces.
|
|
790
|
+
* @returns The entity or undefined if not found.
|
|
791
|
+
*/
|
|
792
|
+
findAssociatedEntity(key: string, namespace?: string): DataEntity | undefined {
|
|
793
|
+
let ns: DataNamespace | undefined
|
|
794
|
+
if (namespace) {
|
|
795
|
+
ns = this.foreign.find((i) => i.key === namespace)
|
|
796
|
+
} else {
|
|
797
|
+
ns = this
|
|
798
|
+
}
|
|
799
|
+
if (!ns) {
|
|
800
|
+
return undefined
|
|
801
|
+
}
|
|
802
|
+
return ns.findEntity(key)
|
|
803
|
+
}
|
|
804
|
+
|
|
718
805
|
addForeign(ns: DataNamespace): void {
|
|
719
806
|
const exists = this.foreign.some((i) => i.key === ns.key)
|
|
720
807
|
if (exists) {
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Data Modeling
|
|
2
|
+
|
|
3
|
+
## Core Concepts
|
|
4
|
+
|
|
5
|
+
At the heart of this data modeling system are the following fundamental concepts.
|
|
6
|
+
|
|
7
|
+
### DataNamespace
|
|
8
|
+
|
|
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
|
+
|
|
11
|
+
- `DataModels`: Logical groupings of entities.
|
|
12
|
+
- `DataEntities`: The basic building blocks, representing individual data structures.
|
|
13
|
+
- `DataProperties`: Attributes of entities (e.g., name, age, address).
|
|
14
|
+
- `DataAssociations`: Relationships between entities (e.g., a user has an address).
|
|
15
|
+
- `Sub-namespaces`: Namespaces can be nested within each other, creating a hierarchical structure.
|
|
16
|
+
- `Foreign Namespaces`: References to external namespaces, enabling the use of entities defined elsewhere.
|
|
17
|
+
- `Tags`: Common tags for the entire namespace.
|
|
18
|
+
|
|
19
|
+
### DataModel
|
|
20
|
+
|
|
21
|
+
A logical grouping of `DataEntity` instances. It represents a specific data structure, like a "Product" or "User" model. A `DataModel` can contain multiple `DataEntity` instances.
|
|
22
|
+
|
|
23
|
+
### DataEntity
|
|
24
|
+
|
|
25
|
+
The fundamental building block of the data model. It represents a specific type of data, like a "User," "Product," or "Address."
|
|
26
|
+
|
|
27
|
+
- `Properties`: DataProperty instances that describe the attributes of the entity.
|
|
28
|
+
- `Associations`: DataAssociation instances that define relationships to other entities.
|
|
29
|
+
- `Parents`: An entity can inherit from other entities, creating a hierarchy.
|
|
30
|
+
- `Fields`: Ordered list of properties and associations.
|
|
31
|
+
- `Tags`: Optional tags for the UI.
|
|
32
|
+
- `Taxonomy`: Reserved for future use.
|
|
33
|
+
- `Deprecated`: Whether the entity is deprecated.
|
|
34
|
+
- `Schema`: The schema allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
|
|
35
|
+
|
|
36
|
+
### DataProperty
|
|
37
|
+
|
|
38
|
+
Represents an attribute of a `DataEntity`. It has a name, a data type (e.g., string, number, boolean), and optional constraints (e.g., required, multiple, min/max values).
|
|
39
|
+
|
|
40
|
+
- `Type`: The data type of the property.
|
|
41
|
+
- `Schema`: The general schema definition of this property.
|
|
42
|
+
- `Bindings`: The list of bindings for this property.
|
|
43
|
+
- `Tags`: Optional tags for the UI.
|
|
44
|
+
- `Taxonomy`: Reserved for future use.
|
|
45
|
+
- `Deprecated`: Whether the property is deprecated.
|
|
46
|
+
- `Primary`: Whether this property describes a primary key of the entity.
|
|
47
|
+
- `Index`: Whether this property describes an indexed property of the entity.
|
|
48
|
+
- `ReadOnly`: Whether the property is read only in the schema.
|
|
49
|
+
- `WriteOnly`: Whether the property is write only in the schema.
|
|
50
|
+
|
|
51
|
+
### DataAssociation
|
|
52
|
+
|
|
53
|
+
Defines a relationship between `DataEntity` instances. It specifies the target entities and the nature of the relationship (e.g., one-to-one, one-to-many).
|
|
54
|
+
|
|
55
|
+
- `Targets`: The list of target entities.
|
|
56
|
+
- `Multiple`: Whether the association allows multiple target entities.
|
|
57
|
+
- `Required`: Whether the association is required.
|
|
58
|
+
- `Schema`: The definition of the database/API schema
|
|
59
|
+
- `Bindings`: The list of bindings allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
|
|
60
|
+
- `Hidden`: Defines if this association is a part of the schema or not.
|
|
61
|
+
|
|
62
|
+
### Bindings
|
|
63
|
+
|
|
64
|
+
Defines a translation from a data model to a specific format (like JSON, RAML, XML, etc.)
|
|
65
|
+
|
|
66
|
+
## How a Data Domain is Structured
|
|
67
|
+
|
|
68
|
+
Here's how these components work together to structure a data domain:
|
|
69
|
+
|
|
70
|
+
1. **Root Namespace**: You start with a `DataNamespace`, which acts as the root of your data domain. This namespace is the top-level container for all other elements.
|
|
71
|
+
|
|
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
|
+
|
|
74
|
+
1. **Data Models**: Within a namespace (or sub-namespace), you define `DataModel` instances. Each `DataModel` represents a specific area of your domain. For example, you might have a "User Management" `DataModel`, a "Product Catalog" `DataModel`, and an "Order Processing" `DataModel`.
|
|
75
|
+
|
|
76
|
+
1. **Data Entities**: Inside each `DataModel`, you define `DataEntity` instances. These are the core data structures. For example, in the "User Management" `DataModel`, you might have `DataEntity` instances for "User," "Role," and "Permission."
|
|
77
|
+
|
|
78
|
+
1. **Data Properties**: Each `DataEntity` has `DataProperty` instances that describe its attributes. For example, the "User" `DataEntity` might have `DataProperty` instances for "firstName" (string), "lastName" (string), "email" (string), "age" (number), etc.
|
|
79
|
+
|
|
80
|
+
1. **Data Associations**: You use `DataAssociation` instances to define relationships between `DataEntity` instances. For example:
|
|
81
|
+
|
|
82
|
+
- A "User" has an "Address" (one-to-one).
|
|
83
|
+
- A "User" has multiple "Roles" (one-to-many).
|
|
84
|
+
- A "Product" belongs to a "Category" (many-to-one).
|
|
85
|
+
- An "Order" contains multiple "Order Items" (one-to-many).
|
|
86
|
+
|
|
87
|
+
1. **Inheritance**: `DataEntity` instances can inherit from other `DataEntity` instances using the parents property. This allows you to create a hierarchy of entities and reuse common properties and associations.
|
|
88
|
+
|
|
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
|
+
|
|
91
|
+
1. **Bindings**: You can define bindings for properties and associations to translate the data model into specific formats (e.g., JSON, RAML, XML, Protocol Buffers).
|
|
92
|
+
|
|
93
|
+
## Example Scenario
|
|
94
|
+
|
|
95
|
+
Let's imagine a simple e-commerce domain:
|
|
96
|
+
|
|
97
|
+
- **Root Namespace**: `ECommerce`
|
|
98
|
+
- **Sub-namespace**: `Catalog`
|
|
99
|
+
- **Data Model**: `ProductCatalog`
|
|
100
|
+
- **Data Entity**: `Product`
|
|
101
|
+
- **Data Properties**: `name` (string), `description` (string), `price` (number), `sku` (string)
|
|
102
|
+
- **Data Association**: `category` (references `Category` entity)
|
|
103
|
+
- **Data Entity**: `Category`
|
|
104
|
+
- **Data Properties**: `name` (string), `description` (string)
|
|
105
|
+
- **Sub-namespace**: `UserManagement`
|
|
106
|
+
- **Data Model**: `UserManagement`
|
|
107
|
+
- **Data Entity**: `User`
|
|
108
|
+
- **Data Properties**: `firstName` (string), `lastName` (string), `email` (string), `password` (string)
|
|
109
|
+
- **Data Association**: `address` (references `Address` entity)
|
|
110
|
+
- **Data Entity**: `Address`
|
|
111
|
+
- **Data Properties**: `street` (string), `city` (string), `zipCode` (string), `country` (string)
|
|
112
|
+
- **Foreign Namespace**: `Taxonomy` (defined elsewhere)
|
|
113
|
+
- **Data Model**: `Taxonomy`
|
|
114
|
+
- **Data Entity**: `TaxonomyItem`
|
|
115
|
+
|
|
116
|
+
### Key Relationships
|
|
117
|
+
|
|
118
|
+
- **Namespace-Model**: A `DataNamespace` contains `DataModel` instances.
|
|
119
|
+
- **Model-Entity**: A `DataModel` contains `DataEntity` instances.
|
|
120
|
+
- **Entity-Property**: A `DataEntity` has `DataProperty` instances.
|
|
121
|
+
- **Entity-Association**: A `DataEntity` has `DataAssociation` instances.
|
|
122
|
+
- **Entity-Parent**: A `DataEntity` can have `DataEntity` instances as parents.
|
|
123
|
+
- **Namespace-Foreign**: A `DataNamespace` can have references to `DataNamespace` instances.
|
|
124
|
+
|
|
125
|
+
## Summary
|
|
126
|
+
|
|
127
|
+
This data modeling system provides a flexible and powerful way to define complex data domains. It allows you to:
|
|
128
|
+
|
|
129
|
+
- **Organize**: Group related data into namespaces and models.
|
|
130
|
+
- **Structure**: Define entities with properties and relationships.
|
|
131
|
+
- **Reuse**: Inherit from other entities and reference foreign namespaces.
|
|
132
|
+
- **Translate**: Define bindings to map the model to different formats.
|
|
133
|
+
- **Validate**: Validate the data model definition.
|
|
134
|
+
- **Generate**: Generate AMF shapes and examples.
|
|
@@ -285,3 +285,57 @@ test.group('breadcrumbs()', (group) => {
|
|
|
285
285
|
assert.equal(result[3].key, m1.key, 'has self last')
|
|
286
286
|
})
|
|
287
287
|
})
|
|
288
|
+
|
|
289
|
+
test.group('adaptEntity()', (group) => {
|
|
290
|
+
let root: DataNamespace
|
|
291
|
+
let dm1: DataModel
|
|
292
|
+
let dm2: DataModel
|
|
293
|
+
let e1: DataEntity
|
|
294
|
+
let e2: DataEntity
|
|
295
|
+
|
|
296
|
+
group.each.setup(() => {
|
|
297
|
+
root = new DataNamespace()
|
|
298
|
+
dm1 = root.addNamespace('dm1').addDataModel('Data Model 1')
|
|
299
|
+
dm2 = root.addNamespace('dm2').addDataModel('Data Model 2')
|
|
300
|
+
e1 = dm1.addEntity('Entity 1')
|
|
301
|
+
e2 = dm1.addEntity('Entity 2')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test('moves an entity to a new data model', ({ assert }) => {
|
|
305
|
+
dm2.adaptEntity(e1)
|
|
306
|
+
assert.deepEqual(dm1.entities, [e2], 'removes from the old data model')
|
|
307
|
+
assert.deepEqual(dm2.entities, [e1], 'adds to the new data model')
|
|
308
|
+
assert.deepEqual(root.definitions.entities, [e1, e2], 'keeps the entity in the root definitions')
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
test('moves an entity to a new index', ({ assert }) => {
|
|
312
|
+
const e3 = dm2.addEntity('Entity 3')
|
|
313
|
+
dm2.adaptEntity(e1, { index: 0 })
|
|
314
|
+
assert.deepEqual(dm1.entities, [e2], 'removes from the old data model')
|
|
315
|
+
assert.deepEqual(dm2.entities, [e1, e3], 'adds to the new data model')
|
|
316
|
+
assert.deepEqual(root.definitions.entities, [e1, e2, e3], 'keeps the entity in the root definitions')
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
test('throws when adapting an entity from another root', ({ assert }) => {
|
|
320
|
+
const otherRoot = new DataNamespace()
|
|
321
|
+
const otherDm = otherRoot.addNamespace('other').addDataModel('Other Data Model')
|
|
322
|
+
const otherEntity = otherDm.addEntity('Other Entity')
|
|
323
|
+
assert.throws(
|
|
324
|
+
() => dm1.adaptEntity(otherEntity),
|
|
325
|
+
`The entity ${otherEntity.key} is not in the same namespace as this data model.`
|
|
326
|
+
)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test('throws when adapting an entity that is already adapted', ({ assert }) => {
|
|
330
|
+
dm2.adaptEntity(e1)
|
|
331
|
+
assert.throws(() => dm2.adaptEntity(e1), `The entity ${e1.key} is already adapted by this data model.`)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
test('throws when index is out of range (minimum)', ({ assert }) => {
|
|
335
|
+
assert.throws(() => dm2.adaptEntity(e1, { index: -1 }), `The index -1 cannot be below 0.`)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
test('throws when index is out of range (maximum)', ({ assert }) => {
|
|
339
|
+
assert.throws(() => dm2.adaptEntity(e1, { index: 1 }), `The index 1 is not valid.`)
|
|
340
|
+
})
|
|
341
|
+
})
|