@api-client/core 0.8.20 → 0.8.22

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 (32) hide show
  1. package/build/browser.d.ts +1 -1
  2. package/build/browser.js.map +1 -1
  3. package/build/index.d.ts +1 -1
  4. package/build/index.js.map +1 -1
  5. package/build/src/models/HttpRequest.d.ts +5 -1
  6. package/build/src/models/HttpRequest.js +13 -1
  7. package/build/src/models/HttpRequest.js.map +1 -1
  8. package/build/src/models/SerializablePayload.d.ts +20 -1
  9. package/build/src/models/SerializablePayload.js +14 -1
  10. package/build/src/models/SerializablePayload.js.map +1 -1
  11. package/build/src/models/data/DataAssociation.d.ts +22 -5
  12. package/build/src/models/data/DataAssociation.js +29 -11
  13. package/build/src/models/data/DataAssociation.js.map +1 -1
  14. package/build/src/models/data/DataEntity.d.ts +12 -1
  15. package/build/src/models/data/DataEntity.js +49 -10
  16. package/build/src/models/data/DataEntity.js.map +1 -1
  17. package/build/src/models/data/DataNamespace.d.ts +34 -1
  18. package/build/src/models/data/DataNamespace.js +75 -1
  19. package/build/src/models/data/DataNamespace.js.map +1 -1
  20. package/build/src/runtime/http-engine/CoreEngine.js +1 -1
  21. package/build/src/runtime/http-engine/CoreEngine.js.map +1 -1
  22. package/build/src/runtime/http-engine/HttpEngine.d.ts +2 -1
  23. package/build/src/runtime/http-engine/HttpEngine.js +22 -15
  24. package/build/src/runtime/http-engine/HttpEngine.js.map +1 -1
  25. package/package.json +12 -13
  26. package/src/models/HttpRequest.ts +17 -2
  27. package/src/models/SerializablePayload.ts +29 -1
  28. package/src/models/data/DataAssociation.ts +50 -15
  29. package/src/models/data/DataEntity.ts +51 -10
  30. package/src/models/data/DataNamespace.ts +78 -2
  31. package/src/runtime/http-engine/CoreEngine.ts +1 -1
  32. package/src/runtime/http-engine/HttpEngine.ts +21 -14
@@ -1,11 +1,30 @@
1
1
  import { PayloadSerializer, Payload, DeserializedPayload } from '../lib/transformers/PayloadSerializer.js';
2
2
  import { hasBuffer } from '../Platform.js';
3
3
 
4
+ export interface SerializablePayloadOptions {
5
+ /**
6
+ * The mime type of the payload, if any.
7
+ * This is a helper information that **should** be put
8
+ * while providing the payload. It is the value of the
9
+ * `content-type` header to be used with the body if one is missing.
10
+ *
11
+ * Note, this is only used when the headers field does not
12
+ * include the `content-type` value.
13
+ */
14
+ mime?: string;
15
+ }
16
+
4
17
  export class SerializablePayload {
5
18
  /**
6
19
  * The serialized payload message.
7
20
  */
8
21
  payload?: Payload;
22
+
23
+ /**
24
+ * Optional meta of the payload
25
+ */
26
+ payloadOptions?: SerializablePayloadOptions;
27
+
9
28
  /**
10
29
  * The payload in its original format.
11
30
  * May not be populated when the `getPayload()` was not yet read.
@@ -15,10 +34,19 @@ export class SerializablePayload {
15
34
  /**
16
35
  * Sets the payload on the request.
17
36
  * This transforms the source `message` to its serialized form using the `PayloadSerializer`.
37
+ *
38
+ * @param message The payload value
39
+ * @param mime The payload mime type. This is optional and sets the `payloadOptions.mime` value when set.
18
40
  */
19
- async writePayload(message: unknown): Promise<void> {
41
+ async writePayload(message: unknown, mime?: string): Promise<void> {
20
42
  this._sourcePayload = message;
21
43
  this.payload = await PayloadSerializer.serialize(message as DeserializedPayload);
44
+ if (mime) {
45
+ if (!this.payloadOptions) {
46
+ this.payloadOptions = {};
47
+ }
48
+ this.payloadOptions.mime = mime;
49
+ }
22
50
  }
23
51
 
24
52
  /**
@@ -8,6 +8,23 @@ import { DataNamespace } from './DataNamespace.js';
8
8
 
9
9
  export const Kind = 'Core#DataAssociation';
10
10
 
11
+ /**
12
+ * Describes association target of an entity
13
+ */
14
+ export interface AssociationTarget {
15
+ /**
16
+ * The key of the associated entity.
17
+ */
18
+ key: string;
19
+ /**
20
+ * The key of the namespace to look for entities.
21
+ * By default it is the current (root) namespace. When this is set the program
22
+ * will look into `foreign` of the root namespace for the definition
23
+ * of the entity.
24
+ */
25
+ namespace?: string;
26
+ }
27
+
11
28
  /**
12
29
  * Describes an association between entities. An association is another property of an entity. The `name` is the name of the
13
30
  * property and the value is the associated target or targets.
@@ -46,7 +63,7 @@ export interface IDataAssociation {
46
63
  * The list of keys associated with the entity through this association.
47
64
  * An association without a target is considered invalid and discarded when processing the values.
48
65
  */
49
- targets?: string[];
66
+ targets?: AssociationTarget[];
50
67
  /**
51
68
  * The schema allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
52
69
  *
@@ -109,7 +126,7 @@ export class DataAssociation {
109
126
  * The list of keys associated with the entity through this association.
110
127
  * An association without a target is considered invalid and discarded when processing the values.
111
128
  */
112
- targets: string[] = [];
129
+ targets: AssociationTarget[] = [];
113
130
 
114
131
  /**
115
132
  * The schema allowing to translate the model into a specific format (like JSON, RAML, XML, etc.)
@@ -145,10 +162,17 @@ export class DataAssociation {
145
162
  *
146
163
  * @param root The namespace root.
147
164
  * @param target The target entity key.
165
+ * @param namespace The optional key of the target association when different to the current one.
148
166
  */
149
- static fromTarget(root: DataNamespace, target: string): DataAssociation {
167
+ static fromTarget(root: DataNamespace, target: string, namespace?: string): DataAssociation {
150
168
  const assoc = new DataAssociation(root);
151
- assoc.targets = [target];
169
+ const info: AssociationTarget = {
170
+ key: target,
171
+ }
172
+ if (namespace) {
173
+ info.namespace = namespace;
174
+ }
175
+ assoc.targets = [info];
152
176
  return assoc;
153
177
  }
154
178
 
@@ -214,7 +238,7 @@ export class DataAssociation {
214
238
  this.required = undefined;
215
239
  }
216
240
  if (Array.isArray(targets)) {
217
- this.targets = [...targets];
241
+ this.targets = targets.map(i => ({ ...i }));
218
242
  } else {
219
243
  this.targets = [];
220
244
  }
@@ -254,7 +278,7 @@ export class DataAssociation {
254
278
  result.required = this.required;
255
279
  }
256
280
  if (Array.isArray(this.targets) && this.targets.length) {
257
- result.targets = [...this.targets];
281
+ result.targets = this.targets.map(i => ({ ...i }));
258
282
  }
259
283
  if (typeof this.hidden === 'boolean') {
260
284
  result.hidden = this.hidden;
@@ -269,8 +293,8 @@ export class DataAssociation {
269
293
  * @returns The list of DataEntity definitions for the current targets.
270
294
  */
271
295
  getTargets(): DataEntity[] {
272
- const { root, targets } = this;
273
- return root.definitions.entities.filter(item => targets.some(i => i === item.key));
296
+ const { targets } = this;
297
+ return this.root.findAssociatedEntities(targets).filter(i => !!i) as DataEntity[];
274
298
  }
275
299
 
276
300
  /**
@@ -293,24 +317,35 @@ export class DataAssociation {
293
317
  * Adds a target to the targets list from an entity.
294
318
  * @param entity The instance or schema of an entity.
295
319
  */
296
- addTarget(entity: DataEntity | IDataEntity): void;
320
+ addTarget(entity: DataEntity | IDataEntity, namespace?: string): void;
297
321
 
298
322
  /**
299
323
  * Adds the target to the targets list with an entity key..
300
324
  * @param key The key of the entity to add.
301
325
  */
302
- addTarget(key: string): void;
326
+ addTarget(key: string, namespace?: string): void;
303
327
 
304
328
  /**
305
329
  * @param init The key of an entity, its instance, or schema.
306
330
  */
307
- addTarget(init: string | DataEntity | IDataEntity): void {
331
+ addTarget(init: string | DataEntity | IDataEntity, namespace?: string): void {
332
+ let key: string;
308
333
  if (typeof init === 'string') {
309
- this.targets.push(init);
334
+ key = init;
310
335
  } else {
311
- const { key } = init;
312
- this.targets.push(key);
336
+ key = init.key;
337
+ const typed = init as DataEntity;
338
+ if (!namespace && typed.root && typed.root !== this.root) {
339
+ namespace = typed.key;
340
+ }
341
+ }
342
+ const info: AssociationTarget = {
343
+ key,
344
+ }
345
+ if (namespace) {
346
+ info.namespace = namespace;
313
347
  }
348
+ this.targets.push(info);
314
349
  }
315
350
 
316
351
  /**
@@ -339,7 +374,7 @@ export class DataAssociation {
339
374
  } else {
340
375
  key = init.key;
341
376
  }
342
- const index = this.targets.indexOf(key);
377
+ const index = this.targets.findIndex(i => i.key === key);
343
378
  if (index >= 0) {
344
379
  this.targets.splice(index, 1);
345
380
  }
@@ -334,6 +334,8 @@ export class DataEntity {
334
334
  /**
335
335
  * Creates an association for a given target, adds it to definitions, and returns it.
336
336
  * @param target The target entity key of the association
337
+ * @param namespace The target entity namespace when different to the current namespace.
338
+ * @param name Optional name.
337
339
  * @returns The created association
338
340
  */
339
341
  addTargetAssociation(target: string, name?: string): DataAssociation {
@@ -346,6 +348,23 @@ export class DataEntity {
346
348
  return result;
347
349
  }
348
350
 
351
+ /**
352
+ * Creates an association for a given target that is in another namespace
353
+ * @param target The target entity key of the association
354
+ * @param namespace The target entity namespace when different to the current namespace.
355
+ * @param name Optional name.
356
+ * @returns The created association
357
+ */
358
+ addForeignAssociation(target: string, namespace: string, name?: string): DataAssociation {
359
+ const result = DataAssociation.fromTarget(this.root, target, namespace);
360
+ if (name) {
361
+ result.info.name = name;
362
+ }
363
+ this.root.definitions.associations.push(result);
364
+ this.associations.push(result);
365
+ return result;
366
+ }
367
+
349
368
  /**
350
369
  * Removes an association from the entity and namespace definitions.
351
370
  * @param key The key of the association to remove.
@@ -395,17 +414,18 @@ export class DataEntity {
395
414
  * Computes a list of entities that are associated with the current entity.
396
415
  */
397
416
  getComputedAssociations(): DataEntity[] {
398
- const { root, associations } = this;
399
- const { entities } = root.definitions;
417
+ const { associations } = this;
400
418
  const result: DataEntity[] = [];
401
419
  associations.forEach((assoc) => {
402
420
  if (!assoc.targets.length) {
403
421
  return;
404
422
  }
405
- const entity = entities.find(i => assoc.targets.includes(i.key));
406
- if (entity) {
407
- result.push(entity);
408
- }
423
+ const entities = this.root.findAssociatedEntities(assoc.targets);
424
+ entities.forEach((entity) => {
425
+ if (entity) {
426
+ result.push(entity);
427
+ }
428
+ });
409
429
  });
410
430
  return result;
411
431
  }
@@ -473,7 +493,7 @@ export class DataEntity {
473
493
  }
474
494
 
475
495
  /**
476
- * Returns a list of entities that are association with this entity through an association
496
+ * Returns a list of entities that are associated with this entity through an association
477
497
  * that the target property points to this entity.
478
498
  *
479
499
  * In other words, if entity `A` has association target with `B`, then asking `B` for related entities will
@@ -489,11 +509,32 @@ export class DataEntity {
489
509
  */
490
510
  getRelatedEntities(): DataEntity[] {
491
511
  const { key, root } = this;
512
+ const result = DataEntity.getRelatedEntities(root, key);
513
+ root.foreign.forEach((ns) => {
514
+ const other = DataEntity.getRelatedEntities(ns, key);
515
+ other.forEach((entity) => {
516
+ if (!result.includes(entity)) {
517
+ result.push(entity);
518
+ }
519
+ });
520
+ });
521
+ return result;
522
+ // const inverse = root.definitions.associations.filter(i => i.targets.some(j => j.key === key));
523
+ // inverse.forEach((assoc) => {
524
+ // const entity = root.definitions.entities.find(e => e.associations.includes(assoc));
525
+ // if (entity) {
526
+ // result.push(entity);
527
+ // }
528
+ // });
529
+ // return result;
530
+ }
531
+
532
+ static getRelatedEntities(namespace: DataNamespace, entity: string): DataEntity[] {
492
533
  const result: DataEntity[] = [];
493
- const inverse = root.definitions.associations.filter(i => i.targets.includes(key));
534
+ const inverse = namespace.definitions.associations.filter(i => i.targets.some(j => j.key === entity));
494
535
  inverse.forEach((assoc) => {
495
- const entity = root.definitions.entities.find(e => e.associations.includes(assoc));
496
- if (entity) {
536
+ const entity = namespace.definitions.entities.find(e => e.associations.includes(assoc));
537
+ if (entity && !result.includes(entity)) {
497
538
  result.push(entity);
498
539
  }
499
540
  });
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-this-alias */
2
2
  import { IThing, Thing } from "../Thing.js";
3
- import { IDataAssociation, DataAssociation } from "./DataAssociation.js";
3
+ import { IDataAssociation, DataAssociation, AssociationTarget } from "./DataAssociation.js";
4
4
  import { IDataEntity, DataEntity } from "./DataEntity.js";
5
5
  import { IDataModel, DataModel, Kind as DataModelKind } from "./DataModel.js";
6
6
  import { IDataProperty, DataProperty } from "./DataProperty.js";
@@ -277,6 +277,16 @@ export class DataItem implements IDataItem {
277
277
  export class DataNamespace extends DataNamespaceParent {
278
278
  definitions: DataDefinitions;
279
279
 
280
+ /**
281
+ * The list of foreign namespaces.
282
+ * This is used to find entities from foreign namespaces.
283
+ * Values here are treated as read only (objects can be frozen).
284
+ *
285
+ * This should be set by the application and the namespace will do
286
+ * nothing to populate this value.
287
+ */
288
+ foreign: DataNamespace[] = [];
289
+
280
290
  /**
281
291
  * Creates a new data namespace from a name.
282
292
  * @param name The name to set.
@@ -553,7 +563,8 @@ export class DataNamespace extends DataNamespaceParent {
553
563
  if (!graph[srcEntity.key]) {
554
564
  graph[srcEntity.key] = [];
555
565
  }
556
- graph[srcEntity.key].splice(0, 0, ...assoc.targets);
566
+ const targetIds = assoc.targets.map(i => i.key);
567
+ graph[srcEntity.key].splice(0, 0, ...targetIds);
557
568
  }
558
569
  return graph;
559
570
  }
@@ -588,4 +599,69 @@ export class DataNamespace extends DataNamespaceParent {
588
599
  path.pop();
589
600
  }
590
601
  }
602
+
603
+ /**
604
+ * Scans all associations for foreign namespaces and returns
605
+ * the list of all namespaces used in the association graph.
606
+ * This will never add this namespace's key.
607
+ *
608
+ * This function should be used to read keys for all related
609
+ * namespaces through associations.
610
+ *
611
+ * @returns All keys of foreign namespaces.
612
+ */
613
+ computeForeignNamespaceKeys(): string[] {
614
+ const result: string[] = [];
615
+ const { associations = [] } = this.definitions;
616
+ associations.forEach((association) => {
617
+ const { targets = [] } = association;
618
+ targets.forEach((target) => {
619
+ const { namespace } = target;
620
+ if (!namespace || namespace === this.key) {
621
+ return;
622
+ }
623
+ result.push(namespace);
624
+ });
625
+ });
626
+ return result;
627
+ }
628
+
629
+ /**
630
+ * Finds an entity in this namespace.
631
+ * @param key The key of the entity to find.
632
+ */
633
+ findEntity(key: string): DataEntity | undefined {
634
+ const { definitions } = this.root || this;
635
+ return definitions.entities.find(i => i.key === key);
636
+ }
637
+
638
+ /**
639
+ * Searches for entities for association targets.
640
+ * This is a helper function to discover entities in the current and foreign namespaces.
641
+ *
642
+ * @param targets The list of targets
643
+ * @returns A list of entities. An `undefined` is put at the index where an entity cannot be found
644
+ */
645
+ findAssociatedEntities(targets: AssociationTarget[]): (DataEntity | undefined)[] {
646
+ const result: (DataEntity | undefined)[] = [];
647
+ targets.forEach((target) => {
648
+ let ns: DataNamespace | undefined;
649
+ if (target.namespace) {
650
+ ns = this.foreign.find(i => i.key === target.namespace);
651
+ } else {
652
+ ns = this;
653
+ }
654
+ if (!ns) {
655
+ result.push(undefined);
656
+ return;
657
+ }
658
+ const entity = ns.findEntity(target.key);
659
+ if (entity) {
660
+ result.push(entity);
661
+ } else {
662
+ result.push(undefined);
663
+ }
664
+ });
665
+ return result;
666
+ }
591
667
  }
@@ -129,7 +129,7 @@ export class CoreEngine extends HttpEngine {
129
129
  payload = undefined;
130
130
  }
131
131
  const headers = new Headers(this.request.toHeadersString());
132
- this.prepareHeaders(headers);
132
+ this.prepareHeaders(headers, !!payload);
133
133
  const auth = this.hasProxy && !this.isProxyTunnel ? this._proxyAuthHeader() : undefined;
134
134
  if (auth) {
135
135
  headers.set('proxy-authorization', auth);
@@ -385,23 +385,30 @@ export abstract class HttpEngine extends EventEmitter {
385
385
  * If `defaultHeaders` option is set then it adds `user-agent` and `accept`
386
386
  * headers.
387
387
  * @param headers Parsed headers
388
+ * @param withPayload Whether the request should send a body message
388
389
  */
389
- prepareHeaders(headers: Headers): void {
390
- if (!this.opts.defaultHeaders) {
391
- return;
392
- }
393
- if (!headers.has('user-agent')) {
394
- if (this.opts.defaultUserAgent) {
395
- headers.set('user-agent', this.opts.defaultUserAgent);
396
- } else {
397
- headers.set('user-agent', 'api client');
390
+ prepareHeaders(headers: Headers, withPayload = false): void {
391
+ if (this.opts.defaultHeaders) {
392
+ if (!headers.has('user-agent')) {
393
+ if (this.opts.defaultUserAgent) {
394
+ headers.set('user-agent', this.opts.defaultUserAgent);
395
+ } else {
396
+ headers.set('user-agent', 'api client');
397
+ }
398
+ }
399
+ if (!headers.has('accept')) {
400
+ if (this.opts.defaultAccept) {
401
+ headers.set('accept', this.opts.defaultAccept);
402
+ } else {
403
+ headers.set('accept', '*/*');
404
+ }
398
405
  }
399
406
  }
400
- if (!headers.has('accept')) {
401
- if (this.opts.defaultAccept) {
402
- headers.set('accept', this.opts.defaultAccept);
403
- } else {
404
- headers.set('accept', '*/*');
407
+ if (withPayload) {
408
+ if (!headers.has('content-type') && this.request.payloadOptions) {
409
+ if (this.request.payloadOptions.mime) {
410
+ headers.set('content-type', this.request.payloadOptions.mime);
411
+ }
405
412
  }
406
413
  }
407
414
  }