@api-client/core 0.18.61 → 0.18.63

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 (52) hide show
  1. package/API_MODELING_EXPLAINER.md +57 -0
  2. package/DATA_MODELING_EXPLAINER.md +74 -0
  3. package/build/src/modeling/DataDomain.d.ts +6 -5
  4. package/build/src/modeling/DataDomain.d.ts.map +1 -1
  5. package/build/src/modeling/DataDomain.js +5 -4
  6. package/build/src/modeling/DataDomain.js.map +1 -1
  7. package/build/src/modeling/DomainAssociation.js +1 -1
  8. package/build/src/modeling/DomainAssociation.js.map +1 -1
  9. package/build/src/modeling/DomainEntity.d.ts +9 -6
  10. package/build/src/modeling/DomainEntity.d.ts.map +1 -1
  11. package/build/src/modeling/DomainEntity.js +34 -18
  12. package/build/src/modeling/DomainEntity.js.map +1 -1
  13. package/build/src/modeling/helpers/Intelisense.d.ts.map +1 -1
  14. package/build/src/modeling/helpers/Intelisense.js +2 -2
  15. package/build/src/modeling/helpers/Intelisense.js.map +1 -1
  16. package/build/src/modeling/importers/JsonSchemaImporter.js +1 -1
  17. package/build/src/modeling/importers/JsonSchemaImporter.js.map +1 -1
  18. package/build/src/modeling/types.d.ts +0 -14
  19. package/build/src/modeling/types.d.ts.map +1 -1
  20. package/build/src/modeling/types.js.map +1 -1
  21. package/build/src/sdk/DataCatalogSdk.d.ts +8 -1
  22. package/build/src/sdk/DataCatalogSdk.d.ts.map +1 -1
  23. package/build/src/sdk/DataCatalogSdk.js +12 -2
  24. package/build/src/sdk/DataCatalogSdk.js.map +1 -1
  25. package/build/tsconfig.tsbuildinfo +1 -1
  26. package/data/models/example-generator-api.json +10 -10
  27. package/package.json +4 -4
  28. package/src/modeling/DataDomain.ts +6 -6
  29. package/src/modeling/DomainAssociation.ts +1 -1
  30. package/src/modeling/DomainEntity.ts +36 -18
  31. package/src/modeling/helpers/Intelisense.ts +8 -14
  32. package/src/modeling/importers/JsonSchemaImporter.ts +1 -1
  33. package/src/modeling/types.ts +0 -15
  34. package/src/sdk/DataCatalogSdk.ts +12 -2
  35. package/tests/unit/modeling/amf/shape_generator.spec.ts +4 -4
  36. package/tests/unit/modeling/api_model_expose_entity.spec.ts +11 -11
  37. package/tests/unit/modeling/api_model_remove_entity.spec.ts +1 -1
  38. package/tests/unit/modeling/data_domain_associations.spec.ts +6 -6
  39. package/tests/unit/modeling/data_domain_foreign.spec.ts +2 -2
  40. package/tests/unit/modeling/data_domain_namespaces.spec.ts +1 -1
  41. package/tests/unit/modeling/data_domain_serialization.spec.ts +15 -15
  42. package/tests/unit/modeling/domain_asociation.spec.ts +15 -15
  43. package/tests/unit/modeling/domain_entity.spec.ts +3 -3
  44. package/tests/unit/modeling/domain_entity_associations.spec.ts +4 -4
  45. package/tests/unit/modeling/domain_entity_example_generator_json.spec.ts +16 -16
  46. package/tests/unit/modeling/domain_entity_example_generator_xml.spec.ts +14 -14
  47. package/tests/unit/modeling/domain_property.spec.ts +1 -1
  48. package/tests/unit/modeling/domain_validation.spec.ts +3 -3
  49. package/tests/unit/modeling/domain_versioning.spec.ts +5 -5
  50. package/tests/unit/modeling/exposed_entity_setter_validation.spec.ts +1 -1
  51. package/tests/unit/modeling/validation/association_validation.spec.ts +40 -56
  52. package/tests/unit/modeling/validation/entity_validation.spec.ts +2 -2
@@ -42810,9 +42810,6 @@
42810
42810
  "@id": "#219"
42811
42811
  },
42812
42812
  {
42813
- "@id": "#219"
42814
- },
42815
- {
42816
42813
  "@id": "#210"
42817
42814
  },
42818
42815
  {
@@ -42820,6 +42817,9 @@
42820
42817
  },
42821
42818
  {
42822
42819
  "@id": "#216"
42820
+ },
42821
+ {
42822
+ "@id": "#219"
42823
42823
  }
42824
42824
  ],
42825
42825
  "doc:root": false,
@@ -44232,7 +44232,7 @@
44232
44232
  "doc:ExternalDomainElement",
44233
44233
  "doc:DomainElement"
44234
44234
  ],
44235
- "doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '21'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)21 302099'\n",
44235
+ "doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '22'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)22 000000'\n",
44236
44236
  "core:mediaType": "application/yaml",
44237
44237
  "sourcemaps:sources": [
44238
44238
  {
@@ -44253,7 +44253,7 @@
44253
44253
  "doc:ExternalDomainElement",
44254
44254
  "doc:DomainElement"
44255
44255
  ],
44256
- "doc:raw": "-\n type: 'GENERAL'\n value: 'info@company.be'\n-\n type: 'IT_DEPT'\n value: 'it-service@company.be'\n",
44256
+ "doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '21'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)21 302099'\n",
44257
44257
  "core:mediaType": "application/yaml",
44258
44258
  "sourcemaps:sources": [
44259
44259
  {
@@ -44274,7 +44274,7 @@
44274
44274
  "doc:ExternalDomainElement",
44275
44275
  "doc:DomainElement"
44276
44276
  ],
44277
- "doc:raw": "type: \"GENERAL\"\nvalue: \"www.company.be\"\n",
44277
+ "doc:raw": "-\n type: 'GENERAL'\n value: 'info@company.be'\n-\n type: 'IT_DEPT'\n value: 'it-service@company.be'\n",
44278
44278
  "core:mediaType": "application/yaml",
44279
44279
  "sourcemaps:sources": [
44280
44280
  {
@@ -44295,7 +44295,7 @@
44295
44295
  "doc:ExternalDomainElement",
44296
44296
  "doc:DomainElement"
44297
44297
  ],
44298
- "doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '22'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)22 000000'\n",
44298
+ "doc:raw": "type: \"GENERAL\"\nvalue: \"www.company.be\"\n",
44299
44299
  "core:mediaType": "application/yaml",
44300
44300
  "sourcemaps:sources": [
44301
44301
  {
@@ -45121,17 +45121,17 @@
45121
45121
  {
45122
45122
  "@id": "#215/source-map/lexical/element_0",
45123
45123
  "sourcemaps:element": "amf://id#215",
45124
- "sourcemaps:value": "[(1,0)-(7,0)]"
45124
+ "sourcemaps:value": "[(1,0)-(6,0)]"
45125
45125
  },
45126
45126
  {
45127
45127
  "@id": "#218/source-map/lexical/element_0",
45128
45128
  "sourcemaps:element": "amf://id#218",
45129
- "sourcemaps:value": "[(1,0)-(3,0)]"
45129
+ "sourcemaps:value": "[(1,0)-(7,0)]"
45130
45130
  },
45131
45131
  {
45132
45132
  "@id": "#221/source-map/lexical/element_0",
45133
45133
  "sourcemaps:element": "amf://id#221",
45134
- "sourcemaps:value": "[(1,0)-(6,0)]"
45134
+ "sourcemaps:value": "[(1,0)-(3,0)]"
45135
45135
  },
45136
45136
  {
45137
45137
  "@id": "#338/source-map/synthesized-field/element_1",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@api-client/core",
3
3
  "description": "The API Client's core client library. Works in NodeJS and in a ES enabled browser.",
4
- "version": "0.18.61",
4
+ "version": "0.18.63",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
7
7
  "./browser.js": {
@@ -108,7 +108,7 @@
108
108
  "devDependencies": {
109
109
  "@commitlint/cli": "^20.1.0",
110
110
  "@commitlint/config-conventional": "^20.0.0",
111
- "@eslint/js": "^9.20.0",
111
+ "@eslint/js": "^10.0.1",
112
112
  "@japa/api-client": "^3.0.3",
113
113
  "@japa/assert": "^4.0.1",
114
114
  "@japa/browser-client": "^2.1.1",
@@ -128,13 +128,13 @@
128
128
  "@web/dev-server-rollup": "^0.6.4",
129
129
  "@web/test-runner": "^0.20.0",
130
130
  "@web/test-runner-playwright": "^0.11.0",
131
- "amf-client-js": "^5.9.1-3",
131
+ "amf-client-js": "^5.10.0-0",
132
132
  "c8": "^10.1.3",
133
133
  "chai-as-promised": "^8.0.2",
134
134
  "conventional-changelog": "^7.1.0",
135
135
  "conventional-changelog-conventionalcommits": "^9.1.0",
136
136
  "cors": "^2.8.5",
137
- "eslint": "^9.20.1",
137
+ "eslint": "^10.0.1",
138
138
  "eslint-config-prettier": "^10.0.1",
139
139
  "eslint-plugin-no-only-tests": "^3.3.0",
140
140
  "eslint-plugin-prettier": "^5.2.3",
@@ -9,7 +9,6 @@ import {
9
9
  DomainPropertyKind,
10
10
  } from '../models/kinds.js'
11
11
  import type {
12
- AssociationAddOptions,
13
12
  DeserializationIssue,
14
13
  DeserializationMode,
15
14
  DomainGraphEdge,
@@ -22,7 +21,7 @@ import type {
22
21
  import { type DomainNamespaceSchema, DomainNamespace, type NamespaceOrderedItem } from './DomainNamespace.js'
23
22
  import { type DomainModelSchema, DomainModel } from './DomainModel.js'
24
23
  import { type DomainEntitySchema, DomainEntity } from './DomainEntity.js'
25
- import { DomainAssociation } from './DomainAssociation.js'
24
+ import { DomainAssociation, DomainAssociationSchema } from './DomainAssociation.js'
26
25
  import { DomainProperty, DomainPropertySchema } from './DomainProperty.js'
27
26
  import { type IThing, Thing } from '../models/Thing.js'
28
27
  import { removeGraphNode } from './GraphUtils.js'
@@ -57,6 +56,7 @@ export interface DataDomainSchema extends DependentModelSchema {
57
56
  * - **Root Container:** Holds all data elements within a domain.
58
57
  * - **Graph-Based Structure:** Uses a graph to represent relationships between data elements.
59
58
  * - **Namespace Management:** Supports creating and managing namespaces to organize data models.
59
+ * Namespaces are optional.
60
60
  * - **Data Model Management:** Supports creating and managing data models to group entities.
61
61
  * - **Entity Management:** Supports creating and managing entities, which define the structure of data.
62
62
  * - **Property Management:** Supports creating and managing properties, which define the data elements
@@ -1084,17 +1084,17 @@ export class DataDomain extends DependentModel {
1084
1084
  * calls the `addAssociation` method on it.
1085
1085
  *
1086
1086
  * @param source The key of the source entity.
1087
- * @param init The association options.
1087
+ * @param input The partial association schema.
1088
1088
  * @returns The created association.
1089
1089
  * @throws Error When the source entity does not exist.
1090
1090
  * @example
1091
1091
  * ```typescript
1092
1092
  * const addressAssociation = dataDomain.addAssociation(
1093
- * 'user', { key: 'address' }
1093
+ * 'user-key', { targets: [{ key: 'address-key' }] }
1094
1094
  * );
1095
1095
  * ```
1096
1096
  */
1097
- addAssociation(source: string, init?: AssociationAddOptions): DomainAssociation {
1097
+ addAssociation(source: string, input?: Partial<DomainAssociationSchema>): DomainAssociation {
1098
1098
  if (!this.graph.hasNode(source)) {
1099
1099
  throw new Error(`Source entity ${source} not found`)
1100
1100
  }
@@ -1105,7 +1105,7 @@ export class DataDomain extends DependentModel {
1105
1105
  if (entity.domain.key !== this.key) {
1106
1106
  throw new Error(`Cannot add an association to a foreign domain`)
1107
1107
  }
1108
- return entity.addAssociation(init)
1108
+ return entity.addAssociation(input)
1109
1109
  }
1110
1110
 
1111
1111
  /**
@@ -625,7 +625,7 @@ export class DomainAssociation extends DomainElement {
625
625
  copy.info = { name: newName }
626
626
  }
627
627
 
628
- const result = parent.addAssociation({}, copy)
628
+ const result = parent.addAssociation(copy)
629
629
 
630
630
  // Copy the target entities
631
631
  for (const target of this.targets) {
@@ -8,7 +8,6 @@ import type { IShapeUnion } from '../amf/definitions/Shapes.js'
8
8
  import { DomainProperty, type DomainPropertySchema } from './DomainProperty.js'
9
9
  import { RemovePropertyException } from '../exceptions/remove_property_exception.js'
10
10
  import { DomainAssociation, DomainAssociationSchema } from './DomainAssociation.js'
11
- import type { AssociationAddOptions } from './types.js'
12
11
  import type { FileBreadcrumb } from '../models/store/File.js'
13
12
  import type { DomainModel } from './DomainModel.js'
14
13
  import type { DomainNamespace } from './DomainNamespace.js'
@@ -628,35 +627,54 @@ export class DomainEntity extends DomainElement {
628
627
  /**
629
628
  * Adds an association to this entity.
630
629
  *
631
- * @param init The association options.
630
+ * Add rules:
631
+ * - If the target is specified, it has to exist in the graph.
632
+ * - The association targets can be local or foreign.
633
+ *
634
+ * Please note that you can't just add targets to the association schema.
635
+ * This function manipulates the graph to insert the required metadata.
636
+ *
632
637
  * @param input The partial association schema.
633
638
  * @returns The created `DomainAssociation` instance.
634
639
  * @throws Error When the target entity does not exist.
635
640
  * @example
636
641
  * ```typescript
637
- * const association = entity.addAssociation({
638
- * key: 'address',
639
- * });
642
+ * const association = entity.addAssociation({ info: { name: 'Address' }, targets: [{ key: 'address' }] });
640
643
  * ```
641
644
  */
642
- addAssociation(init: AssociationAddOptions = {}, input?: Partial<DomainAssociationSchema>): DomainAssociation {
645
+ addAssociation(input: Partial<DomainAssociationSchema> = {}): DomainAssociation {
643
646
  const { graph } = this.root
644
- if (init.domain && init.key) {
645
- // target is foreign
646
- const foreignKey = `${init.domain}:${init.key}`
647
- if (!graph.hasNode(foreignKey)) {
648
- throw new Error(`Foreign entity ${foreignKey} not found`)
649
- }
650
- } else if (init.key) {
651
- if (!graph.hasNode(init.key)) {
652
- throw new Error(`Target entity ${init.key} not found`)
647
+
648
+ // clean up any target information from the input
649
+ // so we won't break the graph and the app.
650
+ const inputCopy = { ...input }
651
+ const targets = inputCopy.targets
652
+ delete inputCopy.targets
653
+
654
+ const hasTargets = Array.isArray(targets) && targets.length
655
+ if (hasTargets) {
656
+ for (const target of targets) {
657
+ if (target.domain && target.key) {
658
+ // target is foreign
659
+ const foreignKey = `${target.domain}:${target.key}`
660
+ if (!graph.hasNode(foreignKey)) {
661
+ throw new Error(`Foreign entity ${foreignKey} not found`)
662
+ }
663
+ } else if (target.key) {
664
+ if (!graph.hasNode(target.key)) {
665
+ throw new Error(`Target entity ${target.key} not found`)
666
+ }
667
+ }
653
668
  }
654
669
  }
655
- const item = new DomainAssociation(this.root, this.key, input)
670
+
671
+ const item = new DomainAssociation(this.root, this.key, inputCopy)
656
672
  graph.setNode(item.key, item)
657
673
  graph.setEdge(this.key, item.key, { type: 'association' })
658
- if (init.key) {
659
- item.addTarget(init.key, init.domain)
674
+ if (hasTargets) {
675
+ for (const target of targets) {
676
+ item.addTarget(target.key, target.domain)
677
+ }
660
678
  }
661
679
  this.fields.push({ type: 'association', key: item.key })
662
680
  this.root.notifyChange()
@@ -325,13 +325,10 @@ export function addOwnerField(entity: DomainEntity, info: Partial<IThing> = {}):
325
325
  if (existing) {
326
326
  return existing
327
327
  }
328
- const prop = entity.addAssociation(
329
- {},
330
- {
331
- info: { name: 'owner', displayName: 'Owner', ...info },
332
- required: true,
333
- }
334
- )
328
+ const prop = entity.addAssociation({
329
+ info: { name: 'owner', displayName: 'Owner', ...info },
330
+ required: true,
331
+ })
335
332
  prop.addSemantic({
336
333
  id: SemanticType.ResourceOwnerIdentifier,
337
334
  })
@@ -378,13 +375,10 @@ export function addUpdatedByField(entity: DomainEntity, info: Partial<IThing> =
378
375
  if (existing) {
379
376
  return existing
380
377
  }
381
- const prop = entity.addAssociation(
382
- {},
383
- {
384
- info: { name: 'updated_by', displayName: 'Updated By', ...info },
385
- required: true,
386
- }
387
- )
378
+ const prop = entity.addAssociation({
379
+ info: { name: 'updated_by', displayName: 'Updated By', ...info },
380
+ required: true,
381
+ })
388
382
  prop.addSemantic({
389
383
  id: SemanticType.ResourceOwnerIdentifier,
390
384
  })
@@ -455,7 +455,7 @@ export class JsonSchemaImporter {
455
455
  // Case 2: If any `$ref` was found, we treat it as an Association.
456
456
  if (analysis.refs.size > 0) {
457
457
  const sanitizedName = toDatabaseColumnName(propName, 'untitled_association')
458
- const assoc = entity.addAssociation({}, { info: { name: sanitizedName } })
458
+ const assoc = entity.addAssociation({ info: { name: sanitizedName } })
459
459
  if (sanitizedName !== propName) {
460
460
  assoc.info.displayName = propName // Keep original name for display
461
461
  }
@@ -33,21 +33,6 @@ export interface DataDomainRemoveOptions {
33
33
  force?: boolean
34
34
  }
35
35
 
36
- export interface AssociationAddOptions {
37
- /**
38
- * When set, it is the the association target's origin data domain.
39
- * The key of the target data domain where this target entity is defined.
40
- * Only used when `key` is specified.
41
- */
42
- domain?: string
43
- /**
44
- * The key of the association target, if known.
45
- * If the target of the association is not specified, and the association has no other targets,
46
- * the association is ignored in the namespace while processing.
47
- */
48
- key?: string
49
- }
50
-
51
36
  export interface DomainGraphEdge {
52
37
  /**
53
38
  * Indicates that the edge is to or from a foreign domain.
@@ -388,9 +388,19 @@ export class DataCatalogSdk extends SdkBase {
388
388
  return data
389
389
  }
390
390
 
391
- async checkPublicationStatus(domainId: string, request: SdkOptions = {}): Promise<DataCatalogStatus> {
391
+ /**
392
+ * Checks the publication status of a data domain.
393
+ * @param domainKey The key of the data domain to check.
394
+ * @param oid The optional ID of the organization. Optional for public domains.
395
+ * @param request The request options.
396
+ * @returns The publication status of the data domain.
397
+ */
398
+ async checkPublicationStatus(domainKey: string, oid?: string, request: SdkOptions = {}): Promise<DataCatalogStatus> {
392
399
  const { token } = request
393
- const url = this.sdk.getUrl(RouteBuilder.dataCatalogStatus(domainId))
400
+ const url = this.sdk.getUrl(RouteBuilder.dataCatalogStatus(domainKey))
401
+ if (oid) {
402
+ url.searchParams.set('oid', oid)
403
+ }
394
404
  const result = await this.sdk.http.get(url.toString(), { token })
395
405
  this.inspectCommonStatusCodes(result)
396
406
  const E_PREFIX = 'Unable to check data domain publication status. '
@@ -95,7 +95,7 @@ test.group('entity()', (group) => {
95
95
 
96
96
  test('adds associations to the properties', ({ assert }) => {
97
97
  e1.addProperty({ key: 'test', info: { name: 'test' } })
98
- e1.addAssociation({}, { info: { name: 'test association' } })
98
+ e1.addAssociation({ info: { name: 'test association' } })
99
99
  const result = e1.toApiShape() as IApiNodeShape
100
100
 
101
101
  assert.lengthOf(result.properties, 2)
@@ -777,7 +777,7 @@ test.group('associationProperty()', (group) => {
777
777
  m1 = root.addModel({ key: 'm1' })
778
778
  e1 = m1.addEntity({ key: 'e1', info: { name: 'e1' } })
779
779
  e2 = m1.addEntity({ key: 'e2', info: { name: 'e2' } })
780
- a1 = e1.addAssociation({ key: e2.key }, { key: 'a1', info: { name: 'a1' } })
780
+ a1 = e1.addAssociation({ targets: [{ key: e2.key }], key: 'a1', info: { name: 'a1' } })
781
781
  e1.addProperty({ key: 'p1', type: 'boolean', info: { name: 'p1' } })
782
782
  p2 = e2.addProperty({ key: 'p2', type: 'number', info: { name: 'p2' } })
783
783
  })
@@ -1048,7 +1048,7 @@ test.group('AI Generated tests', (group) => {
1048
1048
  m1 = root.addModel({ key: 'm1' })
1049
1049
  e1 = m1.addEntity({ key: 'e1', info: { name: 'e1' } })
1050
1050
  e2 = m1.addEntity({ key: 'e2', info: { name: 'e2' } })
1051
- a1 = e1.addAssociation({ key: e2.key }, { key: 'a1', info: { name: 'a1' } })
1051
+ a1 = e1.addAssociation({ targets: [{ key: e2.key }], key: 'a1', info: { name: 'a1' } })
1052
1052
  p1 = e1.addProperty({ key: 'p1', info: { name: 'p1' } })
1053
1053
  })
1054
1054
 
@@ -1067,7 +1067,7 @@ test.group('AI Generated tests', (group) => {
1067
1067
  const e3 = m2.addEntity({ key: 'e3', info: { name: 'e3' } })
1068
1068
  fd1.info.version = '1.0.0'
1069
1069
  root.registerForeignDomain(fd1)
1070
- const a2 = e1.addAssociation({ key: e3.key, domain: fd1.key }, { key: 'a2', info: { name: 'a2' } })
1070
+ const a2 = e1.addAssociation({ targets: [{ key: e3.key, domain: fd1.key }], key: 'a2', info: { name: 'a2' } })
1071
1071
  const result = e1.toApiShape() as IApiNodeShape
1072
1072
  assert.lengthOf(result.properties, 3)
1073
1073
  assert.isTrue(result.properties.some((prop) => prop.id === a2.key))
@@ -72,7 +72,7 @@ test.group('ApiModel.exposeEntity()', () => {
72
72
  const eA = dm.addEntity({ info: { name: 'A' } })
73
73
  const eB = dm.addEntity({ info: { name: 'B' } })
74
74
  // Add association from A to B
75
- eA.addAssociation({ key: eB.key }, { info: { name: 'entityB' } })
75
+ eA.addAssociation({ info: { name: 'entityB' }, targets: [{ key: eB.key }] })
76
76
  const model = new ApiModel()
77
77
  model.attachDataDomain(domain)
78
78
  const exposedA = model.exposeEntity({ key: eA.key }, { followAssociations: true })
@@ -90,8 +90,8 @@ test.group('ApiModel.exposeEntity()', () => {
90
90
  const eA = dm.addEntity({ info: { name: 'A' } })
91
91
  const eB = dm.addEntity({ info: { name: 'B' } })
92
92
  // A -> B, B -> A
93
- eA.addAssociation({ key: eB.key }, { info: { name: 'assocAB' } })
94
- eB.addAssociation({ key: eA.key }, { info: { name: 'assocBA' } })
93
+ eA.addAssociation({ info: { name: 'assocAB' }, targets: [{ key: eB.key }] })
94
+ eB.addAssociation({ info: { name: 'assocBA' }, targets: [{ key: eA.key }] })
95
95
  const model = new ApiModel()
96
96
  model.attachDataDomain(domain)
97
97
  model.exposeEntity({ key: eA.key }, { followAssociations: true, maxDepth: 4 })
@@ -114,7 +114,7 @@ test.group('ApiModel.exposeEntity()', () => {
114
114
  const dm = domain.addModel()
115
115
  const eA = dm.addEntity({ info: { name: 'A' } })
116
116
  // A -> A (self-association)
117
- eA.addAssociation({ key: eA.key }, { info: { name: 'assocAA' } })
117
+ eA.addAssociation({ info: { name: 'assocAA' }, targets: [{ key: eA.key }] })
118
118
  const model = new ApiModel()
119
119
  model.attachDataDomain(domain)
120
120
  model.exposeEntity({ key: eA.key }, { followAssociations: true })
@@ -132,8 +132,8 @@ test.group('ApiModel.exposeEntity()', () => {
132
132
  const eA = dm.addEntity({ info: { name: 'A' } })
133
133
  const eB = dm.addEntity({ info: { name: 'B' } })
134
134
  const eC = dm.addEntity({ info: { name: 'C' } })
135
- eA.addAssociation({ key: eB.key }, { info: { name: 'toB' } })
136
- eB.addAssociation({ key: eC.key }, { info: { name: 'toC' } })
135
+ eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
136
+ eB.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
137
137
  const model = new ApiModel()
138
138
  model.attachDataDomain(domain)
139
139
  model.exposeEntity({ key: eA.key }, { followAssociations: true })
@@ -152,8 +152,8 @@ test.group('ApiModel.exposeEntity()', () => {
152
152
  const eA = dm.addEntity({ info: { name: 'A' } })
153
153
  const eB = dm.addEntity({ info: { name: 'B' } })
154
154
  const eC = dm.addEntity({ info: { name: 'C' } })
155
- eA.addAssociation({ key: eB.key }, { info: { name: 'toB' } })
156
- eA.addAssociation({ key: eC.key }, { info: { name: 'toC' } })
155
+ eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
156
+ eA.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
157
157
  const model = new ApiModel()
158
158
  model.attachDataDomain(domain)
159
159
  model.exposeEntity({ key: eA.key }, { followAssociations: true })
@@ -173,9 +173,9 @@ test.group('ApiModel.exposeEntity()', () => {
173
173
  const eB = dm.addEntity({ info: { name: 'B' } })
174
174
  const eC = dm.addEntity({ info: { name: 'C' } })
175
175
  const eD = dm.addEntity({ info: { name: 'D' } })
176
- eA.addAssociation({ key: eB.key }, { info: { name: 'toB' } })
177
- eB.addAssociation({ key: eC.key }, { info: { name: 'toC' } })
178
- eC.addAssociation({ key: eD.key }, { info: { name: 'toD' } })
176
+ eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
177
+ eB.addAssociation({ info: { name: 'toC' }, targets: [{ key: eC.key }] })
178
+ eC.addAssociation({ info: { name: 'toD' }, targets: [{ key: eD.key }] })
179
179
  const model = new ApiModel()
180
180
  model.attachDataDomain(domain)
181
181
  model.exposeEntity({ key: eA.key }, { followAssociations: true, maxDepth: 2 })
@@ -23,7 +23,7 @@ test.group('ApiModel.removeEntity()', () => {
23
23
  const eA = dm.addEntity({ info: { name: 'A' } })
24
24
  const eB = dm.addEntity({ info: { name: 'B' } })
25
25
  // A -> B
26
- eA.addAssociation({ key: eB.key }, { info: { name: 'toB' } })
26
+ eA.addAssociation({ info: { name: 'toB' }, targets: [{ key: eB.key }] })
27
27
  const model = new ApiModel()
28
28
  model.attachDataDomain(domain)
29
29
  const rootExposure = model.exposeEntity({ key: eA.key }, { followAssociations: true })
@@ -17,7 +17,7 @@ test.group('DataDomain.addAssociation()', () => {
17
17
  const model = dataDomain.addModel({ key: 'test-model' })
18
18
  const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
19
19
  const entity2 = dataDomain.addEntity(model.key, { key: 'entity2' })
20
- const association = dataDomain.addAssociation(entity1.key, { key: entity2.key })
20
+ const association = dataDomain.addAssociation(entity1.key, { targets: [{ key: entity2.key }] })
21
21
  assert.instanceOf(association, DomainAssociation)
22
22
  assert.isTrue(dataDomain.graph.hasNode(association.key))
23
23
  }).tags(['@modeling', '@associations', '@add'])
@@ -84,7 +84,7 @@ test.group('DataDomain.addAssociation()', () => {
84
84
 
85
85
  domain1.registerForeignDomain(domain2)
86
86
 
87
- const association = domain1.addAssociation(e1.key, { key: 'entity2', domain: domain2.key })
87
+ const association = domain1.addAssociation(e1.key, { targets: [{ key: 'entity2', domain: domain2.key }] })
88
88
  assert.instanceOf(association, DomainAssociation)
89
89
  assert.isTrue(domain1.graph.hasNode(association.key))
90
90
  }).tags(['@modeling', '@associations', '@add'])
@@ -99,7 +99,7 @@ test.group('DataDomain.addAssociation()', () => {
99
99
  domain1.registerForeignDomain(domain2)
100
100
 
101
101
  assert.throws(() => {
102
- domain1.addAssociation(e1.key, { key: 'non-existent-entity', domain: 'domain2' })
102
+ domain1.addAssociation(e1.key, { targets: [{ key: 'non-existent-entity', domain: 'domain2' }] })
103
103
  }, 'Foreign entity domain2:non-existent-entity not found')
104
104
  }).tags(['@modeling', '@associations', '@add'])
105
105
  })
@@ -159,7 +159,7 @@ test.group('DataDomain.removeAssociation()', () => {
159
159
  domain2.info.version = '1.0.0'
160
160
  domain1.registerForeignDomain(domain2)
161
161
 
162
- const association = domain1.addAssociation(e1.key, { key: e2.key, domain: domain2.key })
162
+ const association = domain1.addAssociation(e1.key, { targets: [{ key: e2.key, domain: domain2.key }] })
163
163
  domain1.removeAssociation(association.key)
164
164
  assert.isFalse(domain1.graph.hasNode(association.key))
165
165
  }).tags(['@modeling', '@associations', '@remove'])
@@ -187,7 +187,7 @@ test.group('DataDomain.removeAssociation()', () => {
187
187
  const model = dataDomain.addModel()
188
188
  const entity1 = dataDomain.addEntity(model.key, { key: 'entity1' })
189
189
  const entity2 = dataDomain.addEntity(model.key, { key: 'entity2' })
190
- const association = dataDomain.addAssociation(entity1.key, { key: entity2.key })
190
+ const association = dataDomain.addAssociation(entity1.key, { targets: [{ key: entity2.key }] })
191
191
  dataDomain.removeAssociation(association.key)
192
192
  assert.isTrue(dataDomain.graph.hasNode(entity1.key))
193
193
  assert.isTrue(dataDomain.graph.hasNode(entity2.key))
@@ -282,7 +282,7 @@ test.group('DataDomain.findAssociation()', () => {
282
282
  const e2 = domain2.addEntity(m2.key, { key: 'entity2' })
283
283
  domain2.info.version = '1.0.0'
284
284
  domain1.registerForeignDomain(domain2)
285
- const association = domain1.addAssociation(e1.key, { key: e2.key, domain: domain2.key })
285
+ const association = domain1.addAssociation(e1.key, { targets: [{ key: e2.key, domain: domain2.key }] })
286
286
 
287
287
  const foundAssociation = domain1.findAssociation(association.key)
288
288
  assert.deepEqual(foundAssociation, association)
@@ -118,7 +118,7 @@ test.group('DataDomain.registerForeignDomain()', () => {
118
118
  const model = foreignDomain.addModel({ key: 'test-model' })
119
119
  const entity1 = foreignDomain.addEntity(model.key, { key: 'entity1' })
120
120
  const entity2 = foreignDomain.addEntity(model.key, { key: 'entity2' })
121
- const association = entity1.addAssociation({ key: entity2.key })
121
+ const association = entity1.addAssociation({ targets: [{ key: entity2.key }] })
122
122
  foreignDomain.info.version = '1.0.0'
123
123
  dataDomain.registerForeignDomain(foreignDomain)
124
124
 
@@ -606,7 +606,7 @@ test.group('DataDomain.findAssociation()', () => {
606
606
  const m1 = fd.addModel()
607
607
  const e1 = m1.addEntity()
608
608
  const e2 = m1.addEntity()
609
- const a1 = e1.addAssociation({ key: e2.key })
609
+ const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
610
610
  fd.info.version = '1.0.0'
611
611
  domain.registerForeignDomain(fd)
612
612
  const result = domain.findAssociation(a1.key, fd.key)
@@ -143,7 +143,7 @@ test.group('DataDomain.removeNamespace()', () => {
143
143
  const e1 = m1.addEntity({ key: 'entity1' })
144
144
  const e2 = m1.addEntity({ key: 'entity2' })
145
145
  const p1 = e1.addProperty({ key: 'property1' })
146
- const a1 = e1.addAssociation({ key: e2.key })
146
+ const a1 = e1.addAssociation({ targets: [{ key: e2.key }] })
147
147
  d1.removeNamespace(ns1.key)
148
148
  assert.isFalse(d1.graph.hasNode(ns1.key))
149
149
  assert.isFalse(d1.graph.hasNode(ns2.key))