@api-client/core 0.11.3 → 0.11.6

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 (31) hide show
  1. package/build/src/modeling/DataAssociation.d.ts +5 -1
  2. package/build/src/modeling/DataAssociation.d.ts.map +1 -1
  3. package/build/src/modeling/DataAssociation.js +10 -4
  4. package/build/src/modeling/DataAssociation.js.map +1 -1
  5. package/build/src/modeling/DataEntity.d.ts +5 -1
  6. package/build/src/modeling/DataEntity.d.ts.map +1 -1
  7. package/build/src/modeling/DataEntity.js +15 -8
  8. package/build/src/modeling/DataEntity.js.map +1 -1
  9. package/build/src/modeling/DataModel.d.ts +15 -2
  10. package/build/src/modeling/DataModel.d.ts.map +1 -1
  11. package/build/src/modeling/DataModel.js +47 -4
  12. package/build/src/modeling/DataModel.js.map +1 -1
  13. package/build/src/modeling/DataNamespace.d.ts +26 -1
  14. package/build/src/modeling/DataNamespace.d.ts.map +1 -1
  15. package/build/src/modeling/DataNamespace.js +89 -1
  16. package/build/src/modeling/DataNamespace.js.map +1 -1
  17. package/build/src/modeling/DataProperty.js +2 -2
  18. package/build/src/modeling/DataProperty.js.map +1 -1
  19. package/data/models/example-generator-api.json +6 -6
  20. package/data/models/multiple-servers.json +16 -0
  21. package/data/models/petstore.json +92 -7
  22. package/package.json +1 -1
  23. package/src/modeling/DataAssociation.ts +11 -4
  24. package/src/modeling/DataEntity.ts +16 -8
  25. package/src/modeling/DataModel.ts +49 -5
  26. package/src/modeling/DataNamespace.ts +97 -1
  27. package/src/modeling/DataProperty.ts +2 -2
  28. package/tests/unit/modeling/data_association.spec.ts +2 -2
  29. package/tests/unit/modeling/data_entity.spec.ts +3 -3
  30. package/tests/unit/modeling/data_model.spec.ts +57 -3
  31. package/tests/unit/modeling/data_namespace.spec.ts +210 -4
@@ -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[]
@@ -120,9 +127,16 @@ class DataNamespaceParent {
120
127
  }
121
128
 
122
129
  /**
123
- * @returns The parent namespace of this namespace. It returns `undefined` when this is the root namespace.
130
+ * @deprecated Use the `getParentInstance()` method instead.
124
131
  */
125
132
  getParent(): DataNamespace | undefined {
133
+ return this.getParentInstance()
134
+ }
135
+
136
+ /**
137
+ * @returns The parent namespace of this namespace. It returns `undefined` when this is the root namespace.
138
+ */
139
+ getParentInstance(): DataNamespace | undefined {
126
140
  const { root, key } = this
127
141
  if (root) {
128
142
  const result = (root as DataNamespace).findParent(key)
@@ -494,6 +508,88 @@ export class DataNamespace extends DataNamespaceParent {
494
508
  return definition
495
509
  }
496
510
 
511
+ /**
512
+ * Adapts an existing namespace to this namespace.
513
+ * This will remove the namespace from the parent namespace and add it to this one.
514
+ *
515
+ * @param ns The namespace to adapt.
516
+ * @param opts Adapting options.
517
+ */
518
+ adaptNamespace(ns: DataNamespace, opts: DataItemAdaptingOptions = {}): void {
519
+ if (opts.index !== undefined) {
520
+ if (opts.index < 0) {
521
+ throw new Error(`The index ${opts.index} cannot be below 0.`)
522
+ }
523
+ if (opts.index >= this.items.length) {
524
+ throw new Error(`The index ${opts.index} is not valid.`)
525
+ }
526
+ }
527
+ if (ns === this) {
528
+ throw new Error(`Unable to adapt a namespace that is self.`)
529
+ }
530
+ if (ns.root !== this.root) {
531
+ throw new Error(`The namespace ${ns.key} is not in the same namespace as this data namespace.`)
532
+ }
533
+ if (ns.getParentInstance() === this) {
534
+ throw new Error(`The namespace ${ns.key} is already adapted by this data namespace.`)
535
+ }
536
+ // Check for circular dependency
537
+ let current: DataNamespace | undefined = this
538
+ while (current) {
539
+ if (current === ns) {
540
+ throw new Error(`Unable to adapt namespace ${ns.key} under itself or one of its children.`)
541
+ }
542
+ current = current.getParentInstance()
543
+ }
544
+ const parent = ns.getParentInstance() || this.root
545
+ if (!parent) {
546
+ throw new Error(`The namespace ${ns.key} has no parent.`)
547
+ }
548
+ // it has a parent (above check) so it has the index.
549
+ const index = parent.items.findIndex((i) => i.key === ns.key)
550
+ const [item] = parent.items.splice(index, 1)
551
+ if (opts.index !== undefined) {
552
+ this.items.splice(opts.index, 0, item)
553
+ } else {
554
+ this.items.push(item)
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Adapts an existing data model to this namespace.
560
+ * This will remove the data model from the parent namespace and add it to this one.
561
+ * @param model The data model to adapt.
562
+ * @param opts The adapting options.
563
+ */
564
+ adaptDataModel(model: DataModel, opts: DataItemAdaptingOptions = {}): void {
565
+ if (opts.index !== undefined) {
566
+ if (opts.index < 0) {
567
+ throw new Error(`The index ${opts.index} cannot be below 0.`)
568
+ }
569
+ if (opts.index >= this.items.length) {
570
+ throw new Error(`The index ${opts.index} is not valid.`)
571
+ }
572
+ }
573
+ if (model.root !== this.root) {
574
+ throw new Error(`The data model ${model.key} is not in the same namespace as this data namespace.`)
575
+ }
576
+ const parent = model.getParentInstance()
577
+ if (!parent) {
578
+ throw new Error(`The data model ${model.key} has no parent.`)
579
+ }
580
+ if (parent === this) {
581
+ throw new Error(`The data model ${model.key} is already adapted by this data namespace.`)
582
+ }
583
+ // it has a parent (above check) so it has the index.
584
+ const index = parent.items.findIndex((i) => i.key === model.key)
585
+ const [item] = parent.items.splice(index, 1)
586
+ if (opts.index !== undefined) {
587
+ this.items.splice(opts.index, 0, item)
588
+ } else {
589
+ this.items.push(item)
590
+ }
591
+ }
592
+
497
593
  /**
498
594
  * Finds a namespace in the definitions.
499
595
  * @param key The key of the namespace to find.
@@ -445,9 +445,9 @@ export class DataProperty {
445
445
  */
446
446
  remove(): void {
447
447
  const { root } = this
448
- const entity = root.definitions.entities.find((i) => i.properties.some((j) => j === this))
448
+ const entity = root.definitions.entities.find((i) => i.properties.some((j) => j.key === this.key))
449
449
  if (entity) {
450
- const assocIndex = entity.properties.findIndex((i) => i === this)
450
+ const assocIndex = entity.properties.findIndex((i) => i.key === this.key)
451
451
  entity.properties.splice(assocIndex, 1)
452
452
  }
453
453
  const defIndex = this.root.definitions.properties.findIndex((i) => i.key === this.key)
@@ -510,7 +510,7 @@ test.group('toApiShape()', (g) => {
510
510
  })
511
511
  })
512
512
 
513
- test.group('getParent()', (g) => {
513
+ test.group('getParentInstance()', (g) => {
514
514
  let root: DataNamespace
515
515
  let m1: DataModel
516
516
  let e1: DataEntity
@@ -525,7 +525,7 @@ test.group('getParent()', (g) => {
525
525
 
526
526
  test('returns the reference to the parent entity', ({ assert }) => {
527
527
  const a1 = e1.addTargetAssociation(e2.key)
528
- const result = a1.getParent()
528
+ const result = a1.getParentInstance()
529
529
  assert.deepEqual(result, e1)
530
530
  })
531
531
  })
@@ -1616,7 +1616,7 @@ test.group('addParent()', (group) => {
1616
1616
  })
1617
1617
  })
1618
1618
 
1619
- test.group('DataEntity - getParent()', (group) => {
1619
+ test.group('getParentInstance()', (group) => {
1620
1620
  let root: DataNamespace
1621
1621
  let model: DataModel
1622
1622
  let entity1: DataEntity
@@ -1628,12 +1628,12 @@ test.group('DataEntity - getParent()', (group) => {
1628
1628
  })
1629
1629
 
1630
1630
  test('returns undefined when the entity is not in a model', ({ assert }) => {
1631
- const result = new DataEntity(root).getParent()
1631
+ const result = new DataEntity(root).getParentInstance()
1632
1632
  assert.isUndefined(result)
1633
1633
  })
1634
1634
 
1635
1635
  test('returns the parent data model', ({ assert }) => {
1636
- const result = entity1.getParent()
1636
+ const result = entity1.getParentInstance()
1637
1637
  assert.instanceOf(result, DataModel)
1638
1638
  })
1639
1639
  })
@@ -227,7 +227,7 @@ test.group('addEntity()', (group) => {
227
227
  })
228
228
  })
229
229
 
230
- test.group('getParent()', (group) => {
230
+ test.group('getParentInstance()', (group) => {
231
231
  let root: DataNamespace
232
232
 
233
233
  group.each.setup(() => {
@@ -236,14 +236,14 @@ test.group('getParent()', (group) => {
236
236
 
237
237
  test('returns the parent as root', ({ assert }) => {
238
238
  const m1 = root.addDataModel('m1')
239
- const result = m1.getParent()
239
+ const result = m1.getParentInstance()
240
240
  assert.isTrue(result === root)
241
241
  })
242
242
 
243
243
  test('returns the parent is as sub-namespace', ({ assert }) => {
244
244
  const n1 = root.addNamespace('n1')
245
245
  const m1 = n1.addDataModel('m1')
246
- const result = m1.getParent()
246
+ const result = m1.getParentInstance()
247
247
  assert.isTrue(result === n1)
248
248
  })
249
249
  })
@@ -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
+ })
@@ -1,5 +1,13 @@
1
1
  import { test } from '@japa/runner'
2
- import { DataEntity, DataModel, DataNamespace, DataNamespaceKind, IDataNamespace, Thing } from '../../../src/index.js'
2
+ import {
3
+ DataEntity,
4
+ DataItem,
5
+ DataModel,
6
+ DataNamespace,
7
+ DataNamespaceKind,
8
+ IDataNamespace,
9
+ Thing,
10
+ } from '../../../src/index.js'
3
11
 
4
12
  test.group('constructor() defaults', () => {
5
13
  test('sets the kind', ({ assert }) => {
@@ -1025,7 +1033,7 @@ test.group('getRoot()', (group) => {
1025
1033
  })
1026
1034
  })
1027
1035
 
1028
- test.group('getParent()', (group) => {
1036
+ test.group('getParentInstance()', (group) => {
1029
1037
  let root: DataNamespace
1030
1038
  let n1: DataNamespace
1031
1039
  let n2: DataNamespace
@@ -1037,12 +1045,12 @@ test.group('getParent()', (group) => {
1037
1045
  })
1038
1046
 
1039
1047
  test('returns undefined when called on the root namespace', ({ assert }) => {
1040
- const result = root.getParent()
1048
+ const result = root.getParentInstance()
1041
1049
  assert.isUndefined(result)
1042
1050
  })
1043
1051
 
1044
1052
  test('returns the parent namespace when called on a sub-namespace', ({ assert }) => {
1045
- const result = n2.getParent()
1053
+ const result = n2.getParentInstance()
1046
1054
  assert.deepEqual(result, n1)
1047
1055
  })
1048
1056
  })
@@ -1066,3 +1074,201 @@ test.group('isRoot()', (group) => {
1066
1074
  assert.isFalse(result)
1067
1075
  })
1068
1076
  })
1077
+
1078
+ test.group('adaptNamespace()', (group) => {
1079
+ let root: DataNamespace
1080
+ let n1: DataNamespace
1081
+ let n2: DataNamespace
1082
+
1083
+ group.each.setup(() => {
1084
+ root = new DataNamespace()
1085
+ n1 = root.addNamespace('n1')
1086
+ n2 = root.addNamespace('n2')
1087
+ })
1088
+
1089
+ test('moves a namespace to a new parent', ({ assert }) => {
1090
+ const n3 = n1.addNamespace('n3')
1091
+ n2.adaptNamespace(n3)
1092
+ assert.deepEqual(n1.items, [], 'removes from the old parent')
1093
+ const item = DataItem.dataNamespace(root, n3.key)
1094
+ assert.deepEqual(n2.items, [item], 'adds to the new parent')
1095
+ assert.deepEqual(root.definitions.namespaces, [n1, n2, n3], 'keeps the namespace in the root definitions')
1096
+ })
1097
+
1098
+ test('moves a namespace to a new index', ({ assert }) => {
1099
+ const n3 = n1.addNamespace('n3')
1100
+ const n4 = n1.addNamespace('n4')
1101
+ const n5 = n2.addNamespace('n5')
1102
+ n2.adaptNamespace(n3, { index: 0 })
1103
+ const item3 = DataItem.dataNamespace(root, n3.key)
1104
+ const item4 = DataItem.dataNamespace(root, n4.key)
1105
+ const item5 = DataItem.dataNamespace(root, n5.key)
1106
+ assert.deepEqual(n1.items, [item4], 'removes from the old parent')
1107
+ assert.deepEqual(n2.items, [item3, item5], 'adds to the new parent')
1108
+ assert.deepEqual(root.definitions.namespaces, [n1, n2, n3, n4, n5], 'keeps the namespace in the root definitions')
1109
+ })
1110
+
1111
+ test('throws when adapting self', ({ assert }) => {
1112
+ assert.throws(() => n1.adaptNamespace(n1), 'Unable to adapt a namespace that is self.')
1113
+ })
1114
+
1115
+ test('throws when adapting a namespace from another root', ({ assert }) => {
1116
+ const otherRoot = new DataNamespace()
1117
+ const otherNs = otherRoot.addNamespace('other')
1118
+ assert.throws(
1119
+ () => n1.adaptNamespace(otherNs),
1120
+ `The namespace ${otherNs.key} is not in the same namespace as this data namespace.`
1121
+ )
1122
+ })
1123
+
1124
+ test('throws when adapting a namespace that is already adapted', ({ assert }) => {
1125
+ const n3 = n1.addNamespace('n3')
1126
+ n2.adaptNamespace(n3)
1127
+ assert.throws(() => n2.adaptNamespace(n3), `The namespace ${n3.key} is already adapted by this data namespace.`)
1128
+ })
1129
+
1130
+ test('throws when index is out of range (minimum)', ({ assert }) => {
1131
+ assert.throws(() => n1.adaptNamespace(n2, { index: -1 }), `The index -1 cannot be below 0.`)
1132
+ })
1133
+
1134
+ test('throws when index is out of range (maximum)', ({ assert }) => {
1135
+ assert.throws(() => n1.adaptNamespace(n2, { index: 1 }), `The index 1 is not valid.`)
1136
+ })
1137
+ })
1138
+
1139
+ test.group('adaptNamespace() - Circular Dependency', (group) => {
1140
+ let root: DataNamespace
1141
+ let n1: DataNamespace
1142
+ let n2: DataNamespace
1143
+ let n3: DataNamespace
1144
+ let n4: DataNamespace
1145
+
1146
+ // Root
1147
+ // ├── n1
1148
+ // │ ├── n3
1149
+ // │ │ └── n4
1150
+ // └── n2
1151
+
1152
+ group.each.setup(() => {
1153
+ root = new DataNamespace()
1154
+ n1 = root.addNamespace('n1')
1155
+ n2 = root.addNamespace('n2')
1156
+ n3 = n1.addNamespace('n3')
1157
+ n4 = n3.addNamespace('n4')
1158
+ })
1159
+
1160
+ test('throws when adapting a namespace under itself', ({ assert }) => {
1161
+ assert.throws(() => n1.adaptNamespace(n1), 'Unable to adapt a namespace that is self.')
1162
+ })
1163
+
1164
+ test('throws when adapting a namespace under its child', ({ assert }) => {
1165
+ assert.throws(
1166
+ () => n3.adaptNamespace(n1),
1167
+ `Unable to adapt namespace ${n1.key} under itself or one of its children.`
1168
+ )
1169
+ })
1170
+
1171
+ test('throws when adapting a namespace under its grandchild', ({ assert }) => {
1172
+ assert.throws(
1173
+ () => n4.adaptNamespace(n1),
1174
+ `Unable to adapt namespace ${n1.key} under itself or one of its children.`
1175
+ )
1176
+ })
1177
+
1178
+ test('does not throw when adapting a namespace under its sibling', ({ assert }) => {
1179
+ const n5 = n1.addNamespace('n5')
1180
+ assert.doesNotThrow(() => n2.adaptNamespace(n5))
1181
+ })
1182
+
1183
+ test("does not throw when adapting a namespace under its sibling's child", ({ assert }) => {
1184
+ const n5 = n1.addNamespace('n5')
1185
+ const n6 = n5.addNamespace('n6')
1186
+ assert.doesNotThrow(() => n2.adaptNamespace(n6))
1187
+ })
1188
+
1189
+ test("does not throw when adapting a namespace under its sibling's parent", ({ assert }) => {
1190
+ const n5 = n1.addNamespace('n5')
1191
+ // Root (before)
1192
+ // ├── n1
1193
+ // │ ├── n3
1194
+ // │ │ └── n4
1195
+ // │ └── n5
1196
+ // └── n2
1197
+ // Root (after)
1198
+ // └── n1
1199
+ // ├── n3
1200
+ // │ └── n4
1201
+ // └── n5
1202
+ // └── n2
1203
+ assert.doesNotThrow(() => n5.adaptNamespace(n2))
1204
+ })
1205
+ })
1206
+
1207
+ test.group('adaptDataModel()', (group) => {
1208
+ let root: DataNamespace
1209
+ let n1: DataNamespace
1210
+ let n2: DataNamespace
1211
+ let m1: DataModel
1212
+ let m2: DataModel
1213
+
1214
+ group.each.setup(() => {
1215
+ root = new DataNamespace()
1216
+ n1 = root.addNamespace('n1')
1217
+ n2 = root.addNamespace('n2')
1218
+ m1 = n1.addDataModel('m1')
1219
+ m2 = n1.addDataModel('m2')
1220
+ m1.addEntity('e1')
1221
+ })
1222
+
1223
+ test('moves a data model to a new parent', ({ assert }) => {
1224
+ n2.adaptDataModel(m1)
1225
+ const item1 = DataItem.dataModel(root, m1.key)
1226
+ const item2 = DataItem.dataModel(root, m2.key)
1227
+ assert.deepEqual(n1.items, [item2], 'removes from the old parent')
1228
+ assert.deepEqual(n2.items, [item1], 'adds to the new parent')
1229
+ assert.deepEqual(root.definitions.models, [m1, m2], 'keeps the data model in the root definitions')
1230
+ })
1231
+
1232
+ test('moves a data model to a new index', ({ assert }) => {
1233
+ const m3 = n2.addDataModel('m3')
1234
+ n2.adaptDataModel(m1, { index: 0 })
1235
+ const item1 = DataItem.dataModel(root, m1.key)
1236
+ const item2 = DataItem.dataModel(root, m2.key)
1237
+ const item3 = DataItem.dataModel(root, m3.key)
1238
+ assert.deepEqual(n1.items, [item2], 'removes from the old parent')
1239
+ assert.deepEqual(n2.items, [item1, item3], 'adds to the new parent')
1240
+ assert.deepEqual(root.definitions.models, [m1, m2, m3], 'keeps the data model in the root definitions')
1241
+ })
1242
+
1243
+ test('throws when adapting a data model from another root', ({ assert }) => {
1244
+ const otherRoot = new DataNamespace()
1245
+ const otherNs = otherRoot.addNamespace('other')
1246
+ const otherModel = otherNs.addDataModel('Other Data Model')
1247
+ assert.throws(
1248
+ () => n1.adaptDataModel(otherModel),
1249
+ `The data model ${otherModel.key} is not in the same namespace as this data namespace.`
1250
+ )
1251
+ })
1252
+
1253
+ test('throws when adapting a data model that is already adapted', ({ assert }) => {
1254
+ n2.adaptDataModel(m1)
1255
+ assert.throws(() => n2.adaptDataModel(m1), `The data model ${m1.key} is already adapted by this data namespace.`)
1256
+ })
1257
+
1258
+ test('throws when index is out of range (minimum)', ({ assert }) => {
1259
+ assert.throws(() => n2.adaptDataModel(m1, { index: -1 }), `The index -1 cannot be below 0.`)
1260
+ })
1261
+
1262
+ test('throws when index is out of range (maximum)', ({ assert }) => {
1263
+ assert.throws(() => n2.adaptDataModel(m1, { index: 1 }), `The index 1 is not valid.`)
1264
+ })
1265
+
1266
+ test('throws when adapting a data model from another namepsace', ({ assert }) => {
1267
+ const otherRoot = new DataNamespace()
1268
+ const otherModel = new DataModel(otherRoot)
1269
+ assert.throws(
1270
+ () => n1.adaptDataModel(otherModel),
1271
+ `The data model ${otherModel.key} is not in the same namespace as this data namespace.`
1272
+ )
1273
+ })
1274
+ })