@api-client/core 0.11.0 → 0.11.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/AmfMixin.d.ts +2 -15
- package/build/src/amf/AmfMixin.d.ts.map +1 -1
- package/build/src/amf/AmfMixin.js +7 -28
- package/build/src/amf/AmfMixin.js.map +1 -1
- package/build/src/modeling/DataAssociation.d.ts +7 -0
- package/build/src/modeling/DataAssociation.d.ts.map +1 -1
- package/build/src/modeling/DataAssociation.js +12 -0
- package/build/src/modeling/DataAssociation.js.map +1 -1
- package/build/src/modeling/DataEntity.d.ts +64 -9
- package/build/src/modeling/DataEntity.d.ts.map +1 -1
- package/build/src/modeling/DataEntity.js +128 -16
- package/build/src/modeling/DataEntity.js.map +1 -1
- package/build/src/modeling/DataFormat.d.ts +8 -0
- package/build/src/modeling/DataFormat.d.ts.map +1 -1
- package/build/src/modeling/DataFormat.js +1 -0
- package/build/src/modeling/DataFormat.js.map +1 -1
- package/build/src/modeling/DataNamespace.d.ts +6 -0
- package/build/src/modeling/DataNamespace.d.ts.map +1 -1
- package/build/src/modeling/DataNamespace.js +9 -0
- package/build/src/modeling/DataNamespace.js.map +1 -1
- package/data/models/example-generator-api.json +6 -6
- package/package.json +17 -3
- package/src/amf/AmfMixin.ts +10 -31
- package/src/modeling/DataAssociation.ts +13 -0
- package/src/modeling/DataEntity.ts +152 -16
- package/src/modeling/DataFormat.ts +10 -0
- package/src/modeling/DataNamespace.ts +13 -0
- package/tests/unit/amf/AmfLoader.ts +20 -31
- package/tests/unit/modeling/data_entity.spec.ts +481 -0
- package/tsconfig.browser.json +2 -1
|
@@ -42470,13 +42470,13 @@
|
|
|
42470
42470
|
"@id": "#219"
|
|
42471
42471
|
},
|
|
42472
42472
|
{
|
|
42473
|
-
"@id": "#
|
|
42473
|
+
"@id": "#213"
|
|
42474
42474
|
},
|
|
42475
42475
|
{
|
|
42476
42476
|
"@id": "#210"
|
|
42477
42477
|
},
|
|
42478
42478
|
{
|
|
42479
|
-
"@id": "#
|
|
42479
|
+
"@id": "#219"
|
|
42480
42480
|
},
|
|
42481
42481
|
{
|
|
42482
42482
|
"@id": "#216"
|
|
@@ -43913,7 +43913,7 @@
|
|
|
43913
43913
|
"doc:ExternalDomainElement",
|
|
43914
43914
|
"doc:DomainElement"
|
|
43915
43915
|
],
|
|
43916
|
-
"doc:raw": "
|
|
43916
|
+
"doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '22'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)22 000000'\n",
|
|
43917
43917
|
"core:mediaType": "application/yaml",
|
|
43918
43918
|
"sourcemaps:sources": [
|
|
43919
43919
|
{
|
|
@@ -43955,7 +43955,7 @@
|
|
|
43955
43955
|
"doc:ExternalDomainElement",
|
|
43956
43956
|
"doc:DomainElement"
|
|
43957
43957
|
],
|
|
43958
|
-
"doc:raw": "type: 'GENERAL'\
|
|
43958
|
+
"doc:raw": "-\n type: 'GENERAL'\n value: 'info@company.be'\n-\n type: 'IT_DEPT'\n value: 'it-service@company.be'\n",
|
|
43959
43959
|
"core:mediaType": "application/yaml",
|
|
43960
43960
|
"sourcemaps:sources": [
|
|
43961
43961
|
{
|
|
@@ -44781,7 +44781,7 @@
|
|
|
44781
44781
|
{
|
|
44782
44782
|
"@id": "#215/source-map/lexical/element_0",
|
|
44783
44783
|
"sourcemaps:element": "amf://id#215",
|
|
44784
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44784
|
+
"sourcemaps:value": "[(1,0)-(6,0)]"
|
|
44785
44785
|
},
|
|
44786
44786
|
{
|
|
44787
44787
|
"@id": "#218/source-map/lexical/element_0",
|
|
@@ -44791,7 +44791,7 @@
|
|
|
44791
44791
|
{
|
|
44792
44792
|
"@id": "#221/source-map/lexical/element_0",
|
|
44793
44793
|
"sourcemaps:element": "amf://id#221",
|
|
44794
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44794
|
+
"sourcemaps:value": "[(1,0)-(7,0)]"
|
|
44795
44795
|
},
|
|
44796
44796
|
{
|
|
44797
44797
|
"@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.11.
|
|
4
|
+
"version": "0.11.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./browser.js": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@pawel-up/data-mock": "^0.3.2",
|
|
59
59
|
"@pawel-up/jexl": "^3.0.0",
|
|
60
60
|
"@xmldom/xmldom": "^0.9.7",
|
|
61
|
-
"amf-json-ld-lib": "^0.0.
|
|
61
|
+
"amf-json-ld-lib": "^0.0.15",
|
|
62
62
|
"console-table-printer": "^2.11.2",
|
|
63
63
|
"dompurify": "^3.1.5",
|
|
64
64
|
"idb-keyval": "^6.2.1",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@web/test-runner": "^0.20.0",
|
|
95
95
|
"@web/test-runner-commands": "^0.9.0",
|
|
96
96
|
"@web/test-runner-playwright": "^0.11.0",
|
|
97
|
-
"amf-client-js": "^5.
|
|
97
|
+
"amf-client-js": "^5.7.0",
|
|
98
98
|
"c8": "^10.1.3",
|
|
99
99
|
"cors": "^2.8.5",
|
|
100
100
|
"eslint": "^9.20.1",
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"wireit": "^0.14.4"
|
|
122
122
|
},
|
|
123
123
|
"scripts": {
|
|
124
|
+
"build:test": "wireit",
|
|
124
125
|
"build:ts": "wireit",
|
|
125
126
|
"build": "npm run build:ts && npm run lint && npm run copy:assets",
|
|
126
127
|
"lint": "wireit",
|
|
@@ -192,6 +193,19 @@
|
|
|
192
193
|
".tsbuildinfo"
|
|
193
194
|
]
|
|
194
195
|
},
|
|
196
|
+
"build:test": {
|
|
197
|
+
"command": "tsc --project tsconfig.browser.json",
|
|
198
|
+
"clean": "if-file-deleted",
|
|
199
|
+
"files": [
|
|
200
|
+
"src/**/*.ts",
|
|
201
|
+
"test/**/*.ts",
|
|
202
|
+
"tsconfig.browser.json"
|
|
203
|
+
],
|
|
204
|
+
"output": [
|
|
205
|
+
".tmp/testing/**",
|
|
206
|
+
".tsbuildinfo"
|
|
207
|
+
]
|
|
208
|
+
},
|
|
195
209
|
"lint": {
|
|
196
210
|
"command": "eslint --color --cache --cache-location .eslintcache .",
|
|
197
211
|
"files": [
|
package/src/amf/AmfMixin.ts
CHANGED
|
@@ -13,9 +13,7 @@ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
13
13
|
License for the specific language governing permissions and limitations under
|
|
14
14
|
the License.
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
// @ts-expect-error This library has no proper types defined.
|
|
18
|
-
import { AmfModelExpander, JsonLdOptions, JsonLd } from 'amf-json-ld-lib'
|
|
16
|
+
import { AmfModelExpander } from 'amf-json-ld-lib'
|
|
19
17
|
import {
|
|
20
18
|
IAmfDocument,
|
|
21
19
|
IAmfDomainElement,
|
|
@@ -78,7 +76,7 @@ export declare class AmfMixinInterface {
|
|
|
78
76
|
/**
|
|
79
77
|
* Expands flattened AMF model
|
|
80
78
|
*/
|
|
81
|
-
_expand(amf:
|
|
79
|
+
_expand(amf: unknown): IAmfDocument
|
|
82
80
|
|
|
83
81
|
/**
|
|
84
82
|
* Returns compact model key for given value.
|
|
@@ -94,7 +92,7 @@ export declare class AmfMixinInterface {
|
|
|
94
92
|
* @param amf AMF json/ld model
|
|
95
93
|
* @returns The API spec
|
|
96
94
|
*/
|
|
97
|
-
_ensureAmfModel(amf:
|
|
95
|
+
_ensureAmfModel(amf: unknown): IAmfDocument | undefined
|
|
98
96
|
|
|
99
97
|
/**
|
|
100
98
|
* Ensures that the value is an array.
|
|
@@ -525,31 +523,20 @@ export declare class AmfMixinInterface {
|
|
|
525
523
|
export const AmfMixin = <T extends Constructor<any>>(superClass: T): Constructor<AmfMixinInterface> & T => {
|
|
526
524
|
class AmfMixin extends superClass {
|
|
527
525
|
_amf?: IAmfDocument
|
|
528
|
-
_flattenedAmf?: any
|
|
529
526
|
__cachedKeys?: any
|
|
527
|
+
__lastSetValue?: unknown
|
|
530
528
|
|
|
531
529
|
get amf(): IAmfDocument | undefined {
|
|
532
530
|
return this._amf
|
|
533
531
|
}
|
|
534
532
|
|
|
535
533
|
set amf(value: IAmfDocument | undefined) {
|
|
536
|
-
|
|
537
|
-
if (old === value) {
|
|
534
|
+
if (this._amf === value || this.__lastSetValue === value) {
|
|
538
535
|
return
|
|
539
536
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
expanded = value
|
|
544
|
-
} else {
|
|
545
|
-
const oldFlattened = this._flattenedAmf
|
|
546
|
-
if (oldFlattened === value) {
|
|
547
|
-
return
|
|
548
|
-
}
|
|
549
|
-
this._flattenedAmf = value
|
|
550
|
-
expanded = this._expand(value)
|
|
551
|
-
}
|
|
552
|
-
// Cached keys cannot be static as this element can be using in the sane
|
|
537
|
+
this.__lastSetValue = value
|
|
538
|
+
const expanded = value ? this._expand(value) : undefined
|
|
539
|
+
// Cached keys cannot be static as this element can be used in the sane
|
|
553
540
|
// document with different AMF models
|
|
554
541
|
this.__cachedKeys = {}
|
|
555
542
|
this._amf = expanded
|
|
@@ -569,16 +556,8 @@ export const AmfMixin = <T extends Constructor<any>>(superClass: T): Constructor
|
|
|
569
556
|
/**
|
|
570
557
|
* Expands flattened AMF model
|
|
571
558
|
*/
|
|
572
|
-
_expand(amf: any):
|
|
573
|
-
AmfModelExpander.
|
|
574
|
-
const linkEmbeddingFilter = (key: string): boolean => !key.endsWith('fixPoint')
|
|
575
|
-
const rootNode = amf['@context'] ? '' : 'amf://id'
|
|
576
|
-
const options = JsonLdOptions.apply()
|
|
577
|
-
.withEmbeddedLinks(linkEmbeddingFilter)
|
|
578
|
-
.withCompactedIris()
|
|
579
|
-
.withExpandedStructure()
|
|
580
|
-
.withRootNode(rootNode)
|
|
581
|
-
return JsonLd.process(amf, options)
|
|
559
|
+
_expand(amf: any): IAmfDocument {
|
|
560
|
+
return AmfModelExpander.expand(amf) as IAmfDocument
|
|
582
561
|
}
|
|
583
562
|
|
|
584
563
|
/**
|
|
@@ -8,6 +8,7 @@ import { DataNamespace } from './DataNamespace.js'
|
|
|
8
8
|
import { type FieldValidationMessage, ValidationError } from '../exceptions/validation_error.js'
|
|
9
9
|
import { DataAssociationKind } from '../models/kinds.js'
|
|
10
10
|
import type { AssociationBinding, AssociationBindings, AssociationWebBindings } from './Bindings.js'
|
|
11
|
+
import { DataAttributeAttributes, type DataAttributeAttribute } from './DataFormat.js'
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Describes association target of an entity
|
|
@@ -484,6 +485,18 @@ export class DataAssociation {
|
|
|
484
485
|
return this.schema
|
|
485
486
|
}
|
|
486
487
|
|
|
488
|
+
/**
|
|
489
|
+
* Checks whether the passed value is one of the supported data proprty attributes.
|
|
490
|
+
* @param value The value to test
|
|
491
|
+
* @returns True when the passed value is one of the supported data proprty attributes.
|
|
492
|
+
*/
|
|
493
|
+
static isValidAttribute(value: unknown): value is DataAttributeAttribute {
|
|
494
|
+
if (typeof value !== 'string') {
|
|
495
|
+
return false
|
|
496
|
+
}
|
|
497
|
+
return DataAttributeAttributes.includes(value as DataAttributeAttribute)
|
|
498
|
+
}
|
|
499
|
+
|
|
487
500
|
/**
|
|
488
501
|
* Creates if not existing and returns web bindings definition.
|
|
489
502
|
* @returns The web binding definition
|
|
@@ -17,6 +17,11 @@ import { ValidationError, type FieldValidationMessage } from '../exceptions/vali
|
|
|
17
17
|
import { RemoveEntityException } from '../exceptions/remove_entity_exception.js'
|
|
18
18
|
import { ForeignAssociationException } from '../exceptions/foreign_association_exception.js'
|
|
19
19
|
|
|
20
|
+
interface OrderedItem {
|
|
21
|
+
type: 'property' | 'association'
|
|
22
|
+
key: string
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
/**
|
|
21
26
|
* Data entity is the smallest description of a data in the system
|
|
22
27
|
* It contains properties and associations. At least one entity describe a data model.
|
|
@@ -54,6 +59,14 @@ export interface IDataEntity {
|
|
|
54
59
|
*/
|
|
55
60
|
associations?: string[]
|
|
56
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The ordered list of fields (properties and associations) in the schema.
|
|
64
|
+
* These only keep references to the properties and associations.
|
|
65
|
+
* The order of the fields is important as it defines the order of the
|
|
66
|
+
* properties in the schema.
|
|
67
|
+
*/
|
|
68
|
+
fields?: OrderedItem[]
|
|
69
|
+
|
|
57
70
|
/**
|
|
58
71
|
* The list of keys of entities that are parents to this entity.
|
|
59
72
|
*
|
|
@@ -62,6 +75,9 @@ export interface IDataEntity {
|
|
|
62
75
|
* with the same name to shadow parent property. When the property is
|
|
63
76
|
* not shadowed this may cause unexpected results as the processing could result
|
|
64
77
|
* with inconsistent definition of a schema because the last read property wins.
|
|
78
|
+
*
|
|
79
|
+
* @todo(jarrodek): This should also hold a reference to a namespace to support
|
|
80
|
+
* foreign entities as parents.
|
|
65
81
|
*/
|
|
66
82
|
parents?: string[]
|
|
67
83
|
|
|
@@ -112,6 +128,14 @@ export class DataEntity {
|
|
|
112
128
|
*/
|
|
113
129
|
associations: DataAssociation[] = []
|
|
114
130
|
|
|
131
|
+
/**
|
|
132
|
+
* The ordered list of fields (properties and associations) in the schema.
|
|
133
|
+
* These only keep references to the properties and associations.
|
|
134
|
+
* The order of the fields is important as it defines the order of the
|
|
135
|
+
* properties in the schema.
|
|
136
|
+
*/
|
|
137
|
+
fields: OrderedItem[] = []
|
|
138
|
+
|
|
115
139
|
/**
|
|
116
140
|
* The list of keys of entities that are parents to this entity.
|
|
117
141
|
*
|
|
@@ -167,6 +191,7 @@ export class DataEntity {
|
|
|
167
191
|
parents,
|
|
168
192
|
properties,
|
|
169
193
|
associations,
|
|
194
|
+
fields,
|
|
170
195
|
deprecated,
|
|
171
196
|
} = init
|
|
172
197
|
this.kind = kind
|
|
@@ -209,6 +234,11 @@ export class DataEntity {
|
|
|
209
234
|
}
|
|
210
235
|
})
|
|
211
236
|
}
|
|
237
|
+
if (Array.isArray(fields)) {
|
|
238
|
+
this.fields = [...fields]
|
|
239
|
+
} else {
|
|
240
|
+
this.fields = this.createOrderedFields()
|
|
241
|
+
}
|
|
212
242
|
if (typeof deprecated === 'boolean') {
|
|
213
243
|
this.deprecated = deprecated
|
|
214
244
|
} else {
|
|
@@ -216,6 +246,23 @@ export class DataEntity {
|
|
|
216
246
|
}
|
|
217
247
|
}
|
|
218
248
|
|
|
249
|
+
private createOrderedFields(): OrderedItem[] {
|
|
250
|
+
const result: OrderedItem[] = []
|
|
251
|
+
this.properties.forEach((i) => {
|
|
252
|
+
result.push({
|
|
253
|
+
type: 'property',
|
|
254
|
+
key: i.key,
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
this.associations.forEach((i) => {
|
|
258
|
+
result.push({
|
|
259
|
+
type: 'association',
|
|
260
|
+
key: i.key,
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
return result
|
|
264
|
+
}
|
|
265
|
+
|
|
219
266
|
static validate(input: unknown): void {
|
|
220
267
|
const typed = input as IDataEntity
|
|
221
268
|
const messages: FieldValidationMessage[] = []
|
|
@@ -260,6 +307,15 @@ export class DataEntity {
|
|
|
260
307
|
if (Array.isArray(this.associations) && this.associations.length) {
|
|
261
308
|
result.associations = this.associations.map((i) => i.key)
|
|
262
309
|
}
|
|
310
|
+
if (Array.isArray(this.fields) && this.fields.length) {
|
|
311
|
+
result.fields = [...this.fields]
|
|
312
|
+
}
|
|
313
|
+
if (Array.isArray(this.taxonomy) && this.taxonomy.length) {
|
|
314
|
+
result.taxonomy = [...this.taxonomy]
|
|
315
|
+
}
|
|
316
|
+
if (Array.isArray(this.tags) && this.tags.length) {
|
|
317
|
+
result.tags = [...this.tags]
|
|
318
|
+
}
|
|
263
319
|
if (typeof this.deprecated === 'boolean') {
|
|
264
320
|
result.deprecated = this.deprecated
|
|
265
321
|
}
|
|
@@ -286,6 +342,7 @@ export class DataEntity {
|
|
|
286
342
|
}
|
|
287
343
|
this.root.definitions.properties.push(property)
|
|
288
344
|
this.properties.push(property)
|
|
345
|
+
this.fields.push({ type: 'property', key: property.key })
|
|
289
346
|
return property
|
|
290
347
|
}
|
|
291
348
|
|
|
@@ -298,6 +355,7 @@ export class DataEntity {
|
|
|
298
355
|
const property = DataProperty.fromName(this.root, name)
|
|
299
356
|
this.root.definitions.properties.push(property)
|
|
300
357
|
this.properties.push(property)
|
|
358
|
+
this.fields.push({ type: 'property', key: property.key })
|
|
301
359
|
return property
|
|
302
360
|
}
|
|
303
361
|
|
|
@@ -316,24 +374,70 @@ export class DataEntity {
|
|
|
316
374
|
if (defIndex >= 0) {
|
|
317
375
|
this.root.definitions.properties.splice(defIndex, 1)
|
|
318
376
|
}
|
|
377
|
+
this.removeField(key)
|
|
319
378
|
propertyToRemove.remove()
|
|
320
379
|
}
|
|
321
380
|
|
|
381
|
+
private removeField(key: string): void {
|
|
382
|
+
this.fields = this.fields.filter((item) => item.key !== key)
|
|
383
|
+
}
|
|
384
|
+
|
|
322
385
|
/**
|
|
323
386
|
* Lists all properties for this Entity.
|
|
324
387
|
* @returns The list of properties that belong to this entity.
|
|
325
388
|
*/
|
|
326
389
|
listProperties(): DataProperty[] {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
390
|
+
return [...this.properties]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Lists all associations for this Entity.
|
|
395
|
+
* @returns The list of associations that belong to this entity.
|
|
396
|
+
*/
|
|
397
|
+
listAssociations(): DataAssociation[] {
|
|
398
|
+
return [...this.associations]
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Generates an ordered list of fields (properties and associations) for this entity.
|
|
403
|
+
*
|
|
404
|
+
* @returns The list of fields (properties and associations) that belong to this entity.
|
|
405
|
+
*/
|
|
406
|
+
listFields(): (DataAssociation | DataProperty)[] {
|
|
407
|
+
const result: (DataAssociation | DataProperty)[] = []
|
|
408
|
+
this.fields.forEach((i) => {
|
|
409
|
+
if (i.type === 'property') {
|
|
410
|
+
const property = this.properties.find((p) => p.key === i.key)
|
|
411
|
+
if (property) {
|
|
412
|
+
result.push(property)
|
|
413
|
+
}
|
|
414
|
+
} else if (i.type === 'association') {
|
|
415
|
+
const association = this.associations.find((a) => a.key === i.key)
|
|
416
|
+
if (association) {
|
|
417
|
+
result.push(association)
|
|
418
|
+
}
|
|
332
419
|
}
|
|
333
|
-
}
|
|
420
|
+
})
|
|
334
421
|
return result
|
|
335
422
|
}
|
|
336
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Moves the field to a new index.
|
|
426
|
+
* @param key The key of the field to move.
|
|
427
|
+
* @param toIndex The new index to which move the field to.
|
|
428
|
+
*/
|
|
429
|
+
moveField(key: string, toIndex: number): void {
|
|
430
|
+
const fromIndex = this.fields.findIndex((i) => i.key === key)
|
|
431
|
+
if (fromIndex < 0) {
|
|
432
|
+
throw new ValidationError([{ field: 'fields', message: `Field ${key} not found.`, rule: 'not-found' }])
|
|
433
|
+
}
|
|
434
|
+
if (toIndex < 0 || toIndex >= this.fields.length) {
|
|
435
|
+
throw new ValidationError([{ field: 'fields', message: `Invalid index ${toIndex}.`, rule: 'invalid' }])
|
|
436
|
+
}
|
|
437
|
+
const [item] = this.fields.splice(fromIndex, 1)
|
|
438
|
+
this.fields.splice(toIndex, 0, item)
|
|
439
|
+
}
|
|
440
|
+
|
|
337
441
|
/**
|
|
338
442
|
* Creates an association for a given name, adds it to definitions, and returns it.
|
|
339
443
|
* @param name The name of the association
|
|
@@ -343,6 +447,7 @@ export class DataEntity {
|
|
|
343
447
|
const result = DataAssociation.fromName(this.root, name)
|
|
344
448
|
this.root.definitions.associations.push(result)
|
|
345
449
|
this.associations.push(result)
|
|
450
|
+
this.fields.push({ type: 'association', key: result.key })
|
|
346
451
|
return result
|
|
347
452
|
}
|
|
348
453
|
|
|
@@ -367,6 +472,7 @@ export class DataEntity {
|
|
|
367
472
|
}
|
|
368
473
|
this.root.definitions.associations.push(result)
|
|
369
474
|
this.associations.push(result)
|
|
475
|
+
this.fields.push({ type: 'association', key: result.key })
|
|
370
476
|
return result
|
|
371
477
|
}
|
|
372
478
|
|
|
@@ -395,6 +501,7 @@ export class DataEntity {
|
|
|
395
501
|
}
|
|
396
502
|
this.root.definitions.associations.push(result)
|
|
397
503
|
this.associations.push(result)
|
|
504
|
+
this.fields.push({ type: 'association', key: result.key })
|
|
398
505
|
return result
|
|
399
506
|
}
|
|
400
507
|
|
|
@@ -414,6 +521,7 @@ export class DataEntity {
|
|
|
414
521
|
if (defIndex >= 0) {
|
|
415
522
|
this.root.definitions.associations.splice(defIndex, 1)
|
|
416
523
|
}
|
|
524
|
+
this.removeField(key)
|
|
417
525
|
associationToRemove.remove()
|
|
418
526
|
}
|
|
419
527
|
|
|
@@ -511,6 +619,12 @@ export class DataEntity {
|
|
|
511
619
|
return this.root.definitions.models.find((m) => m.entities.some((e) => e === this))
|
|
512
620
|
}
|
|
513
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Adds a parent reference to this entity.
|
|
624
|
+
* This will not add the parent to the namespace. It only adds a reference to the parent.
|
|
625
|
+
* @param key The key of the parent entity to add.
|
|
626
|
+
* @returns `this` for chaining.
|
|
627
|
+
*/
|
|
514
628
|
addParent(key: string): this {
|
|
515
629
|
// Prevent adding self as parent
|
|
516
630
|
if (key === this.key) {
|
|
@@ -555,6 +669,20 @@ export class DataEntity {
|
|
|
555
669
|
{ message }
|
|
556
670
|
)
|
|
557
671
|
}
|
|
672
|
+
const has = this.parents.some((i) => i === key)
|
|
673
|
+
if (has) {
|
|
674
|
+
const message = `Parent ${key} already exists.`
|
|
675
|
+
throw new ValidationError(
|
|
676
|
+
[
|
|
677
|
+
{
|
|
678
|
+
field: 'parents',
|
|
679
|
+
message,
|
|
680
|
+
rule: 'exists',
|
|
681
|
+
},
|
|
682
|
+
],
|
|
683
|
+
{ message }
|
|
684
|
+
)
|
|
685
|
+
}
|
|
558
686
|
this.parents.push(key)
|
|
559
687
|
return this
|
|
560
688
|
}
|
|
@@ -622,19 +750,27 @@ export class DataEntity {
|
|
|
622
750
|
}
|
|
623
751
|
|
|
624
752
|
/**
|
|
625
|
-
* Returns a list of entities that are associated with this entity
|
|
626
|
-
* that the target property points to this entity.
|
|
753
|
+
* Returns a list of entities that are associated with this entity as a target.
|
|
627
754
|
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
755
|
+
* This method identifies entities that have an association where this entity is a target.
|
|
756
|
+
* In other words, it finds entities that "point to" this entity through an association.
|
|
757
|
+
*
|
|
758
|
+
* For example, consider the following relationships:
|
|
630
759
|
*
|
|
631
760
|
* ```plain
|
|
632
|
-
* A -> B
|
|
633
|
-
*
|
|
761
|
+
* A -> B (A has an association that targets B)
|
|
762
|
+
* C -> B (C has an association that targets B)
|
|
763
|
+
* B -> D (B has an association that targets D)
|
|
634
764
|
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
765
|
+
* Calling `getRelatedEntities()` on B would return [A, C]
|
|
766
|
+
* Calling `getRelatedEntities()` on D would return [B]
|
|
767
|
+
* Calling `getRelatedEntities()` on A would return []
|
|
637
768
|
* ```
|
|
769
|
+
*
|
|
770
|
+
* Note, this method only returns entities that are directly associated with this entity as a target.
|
|
771
|
+
* It does not traverse the association graph to find indirectly related entities.
|
|
772
|
+
*
|
|
773
|
+
* @returns An array of `DataEntity` instances that have an association targeting this entity.
|
|
638
774
|
*/
|
|
639
775
|
getRelatedEntities(): DataEntity[] {
|
|
640
776
|
const { key, root } = this
|
|
@@ -650,9 +786,9 @@ export class DataEntity {
|
|
|
650
786
|
return result
|
|
651
787
|
}
|
|
652
788
|
|
|
653
|
-
static getRelatedEntities(namespace: DataNamespace,
|
|
789
|
+
static getRelatedEntities(namespace: DataNamespace, entityKey: string): DataEntity[] {
|
|
654
790
|
const result: DataEntity[] = []
|
|
655
|
-
const inverse = namespace.definitions.associations.filter((i) => i.targets.some((j) => j.key ===
|
|
791
|
+
const inverse = namespace.definitions.associations.filter((i) => i.targets.some((j) => j.key === entityKey))
|
|
656
792
|
inverse.forEach((assoc) => {
|
|
657
793
|
const entity = namespace.definitions.entities.find((e) => e.associations.includes(assoc))
|
|
658
794
|
if (entity && !result.includes(entity)) {
|
|
@@ -45,6 +45,9 @@ export enum DataPropertyList {
|
|
|
45
45
|
file = 'binary',
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* The data property "attributes".
|
|
50
|
+
*/
|
|
48
51
|
export type DataPropertyAttribute =
|
|
49
52
|
| 'required'
|
|
50
53
|
| 'multiple'
|
|
@@ -64,6 +67,13 @@ export const DataPropertyAttributes: DataPropertyAttribute[] = [
|
|
|
64
67
|
'deprecated',
|
|
65
68
|
]
|
|
66
69
|
|
|
70
|
+
/**
|
|
71
|
+
* The data attribute "attributes".
|
|
72
|
+
*/
|
|
73
|
+
export type DataAttributeAttribute = 'required' | 'multiple'
|
|
74
|
+
|
|
75
|
+
export const DataAttributeAttributes: DataAttributeAttribute[] = ['required', 'multiple']
|
|
76
|
+
|
|
67
77
|
/**
|
|
68
78
|
* Note, OAS supports the `integer` data type and not format.
|
|
69
79
|
* We need to account for that.
|
|
@@ -24,8 +24,11 @@ interface IDataDefinitions {
|
|
|
24
24
|
|
|
25
25
|
interface DataDefinitions {
|
|
26
26
|
models: DataModel[]
|
|
27
|
+
// @todo: This should be a map of entities with a key of the entity key.
|
|
27
28
|
entities: DataEntity[]
|
|
29
|
+
// @todo: This should be a map of properties with a key of the property key.
|
|
28
30
|
properties: DataProperty[]
|
|
31
|
+
// @todo: This should be a map of associations with a key of the association key.
|
|
29
32
|
associations: DataAssociation[]
|
|
30
33
|
namespaces: DataNamespace[]
|
|
31
34
|
/**
|
|
@@ -672,6 +675,16 @@ export class DataNamespace extends DataNamespaceParent {
|
|
|
672
675
|
return definitions.properties.find((i) => i.key === key)
|
|
673
676
|
}
|
|
674
677
|
|
|
678
|
+
/**
|
|
679
|
+
* Finds an association by its key.
|
|
680
|
+
* @param key The key of the property to find
|
|
681
|
+
* @returns The property or undefined if not found.
|
|
682
|
+
*/
|
|
683
|
+
findAssociation(key: string): DataAssociation | undefined {
|
|
684
|
+
const { definitions } = this.root || this
|
|
685
|
+
return definitions.associations.find((i) => i.key === key)
|
|
686
|
+
}
|
|
687
|
+
|
|
675
688
|
/**
|
|
676
689
|
* Searches for entities for association targets.
|
|
677
690
|
* This is a helper function to discover entities in the current and foreign namespaces.
|