@api-components/api-type-document 4.2.34 → 4.2.37
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/package.json +7 -7
- package/src/ApiTypeDocument.d.ts +26 -0
- package/src/ApiTypeDocument.js +231 -19
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api-components/api-type-document",
|
|
3
3
|
"description": "A documentation table for type (resource) properties. Works with AMF data model",
|
|
4
|
-
"version": "4.2.
|
|
4
|
+
"version": "4.2.37",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"module": "index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@advanced-rest-client/arc-marked": "^1.1.0",
|
|
29
29
|
"@advanced-rest-client/markdown-styles": "^3.1.4",
|
|
30
30
|
"@anypoint-web-components/anypoint-button": "^1.2.3",
|
|
31
|
-
"@api-components/amf-helper-mixin": "^4.5.
|
|
31
|
+
"@api-components/amf-helper-mixin": "^4.5.34",
|
|
32
32
|
"@api-components/api-annotation-document": "^4.1.0",
|
|
33
33
|
"@api-components/api-resource-example-document": "^4.3.3",
|
|
34
34
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"@anypoint-web-components/anypoint-checkbox": "^1.2.2",
|
|
40
40
|
"@anypoint-web-components/anypoint-styles": "^1.0.2",
|
|
41
41
|
"@api-components/api-model-generator": "^0.2.14",
|
|
42
|
-
"@commitlint/cli": "^13.2.
|
|
42
|
+
"@commitlint/cli": "^13.2.1",
|
|
43
43
|
"@commitlint/config-conventional": "^13.2.0",
|
|
44
44
|
"@open-wc/eslint-config": "^4.2.0",
|
|
45
|
-
"@open-wc/testing": "^2.5.
|
|
45
|
+
"@open-wc/testing": "^2.5.32",
|
|
46
46
|
"@web/dev-server": "^0.1.24",
|
|
47
47
|
"@web/test-runner": "^0.13.18",
|
|
48
|
-
"@web/test-runner-playwright": "0.
|
|
49
|
-
"eslint": "^
|
|
50
|
-
"eslint-config-prettier": "^8.
|
|
48
|
+
"@web/test-runner-playwright": "^0.11.0",
|
|
49
|
+
"eslint": "^8.0.0",
|
|
50
|
+
"eslint-config-prettier": "^8.3.0",
|
|
51
51
|
"husky": "^7.0.2",
|
|
52
52
|
"lint-staged": "^11.1.2",
|
|
53
53
|
"sinon": "^11.1.2",
|
package/src/ApiTypeDocument.d.ts
CHANGED
|
@@ -54,6 +54,23 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
54
54
|
selectedMediaType: number;
|
|
55
55
|
// The type after it has been resolved.
|
|
56
56
|
_resolvedType: Object;
|
|
57
|
+
/**
|
|
58
|
+
* Computed properties for object types.
|
|
59
|
+
* This is a reactive property that is recalculated when type, amf, or renderReadOnly changes.
|
|
60
|
+
*/
|
|
61
|
+
_computedProperties: any[] | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Resolved type for examples with all link-target references resolved
|
|
64
|
+
*/
|
|
65
|
+
_resolvedExampleType: Object | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Whether to show the examples section
|
|
68
|
+
*/
|
|
69
|
+
_showExamples: boolean | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Effective media type for examples
|
|
72
|
+
*/
|
|
73
|
+
_exampleMediaType: string | undefined;
|
|
57
74
|
/**
|
|
58
75
|
* Should be set if described properties has a parent type.
|
|
59
76
|
* This is used when recursively iterating over properties.
|
|
@@ -242,6 +259,15 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
242
259
|
*/
|
|
243
260
|
_computeProperties(item: any): any[]|undefined;
|
|
244
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Deeply resolves link-target references in a type and its nested properties.
|
|
264
|
+
* This is essential for rendering complete examples with nested objects.
|
|
265
|
+
*
|
|
266
|
+
* @param type The type to resolve
|
|
267
|
+
* @returns The type with all link-target references resolved
|
|
268
|
+
*/
|
|
269
|
+
_deepResolveType(type: any): any|undefined;
|
|
270
|
+
|
|
245
271
|
/**
|
|
246
272
|
* Computes list values for `andTypes` property.
|
|
247
273
|
* @param items List of OAS' "and" properties
|
package/src/ApiTypeDocument.js
CHANGED
|
@@ -68,6 +68,22 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
68
68
|
selectedMediaType: { type: Number },
|
|
69
69
|
// The type after it has been resolved.
|
|
70
70
|
_resolvedType: { type: Object },
|
|
71
|
+
/**
|
|
72
|
+
* Computed properties from the resolved type
|
|
73
|
+
*/
|
|
74
|
+
_computedProperties: { type: Array },
|
|
75
|
+
/**
|
|
76
|
+
* Resolved type for examples with all link-target references resolved
|
|
77
|
+
*/
|
|
78
|
+
_resolvedExampleType: { type: Object },
|
|
79
|
+
/**
|
|
80
|
+
* Whether to show the examples section
|
|
81
|
+
*/
|
|
82
|
+
_showExamples: { type: Boolean },
|
|
83
|
+
/**
|
|
84
|
+
* Effective media type for examples
|
|
85
|
+
*/
|
|
86
|
+
_exampleMediaType: { type: String },
|
|
71
87
|
/**
|
|
72
88
|
* Should be set if described properties has a parent type.
|
|
73
89
|
* This is used when recursively iterating over properties.
|
|
@@ -325,6 +341,10 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
325
341
|
*/
|
|
326
342
|
this.selectedAnyOf = undefined;
|
|
327
343
|
this.renderReadOnly = false;
|
|
344
|
+
this.noMainExample = false;
|
|
345
|
+
this._hasExamples = false;
|
|
346
|
+
this._renderMainExample = false;
|
|
347
|
+
this._cachedDeepResolvedType = undefined;
|
|
328
348
|
|
|
329
349
|
this._isPropertyReadOnly = this._isPropertyReadOnly.bind(this);
|
|
330
350
|
this.noMediaSelector = false;
|
|
@@ -345,6 +365,37 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
345
365
|
return isScalar ? false : !!(!noMainExample && hasExamples);
|
|
346
366
|
}
|
|
347
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Called when properties change
|
|
370
|
+
* @param {Map} changedProperties Changed properties
|
|
371
|
+
*/
|
|
372
|
+
updated(changedProperties) {
|
|
373
|
+
super.updated(changedProperties);
|
|
374
|
+
|
|
375
|
+
// If amf changed and we have a type, recalculate properties synchronously
|
|
376
|
+
if (changedProperties.has('amf') && this._resolvedType && this.amf) {
|
|
377
|
+
// Cancel any pending debounced calls and recalculate
|
|
378
|
+
this.__typeChangeDebouncer = false;
|
|
379
|
+
this._typeChanged(this._resolvedType);
|
|
380
|
+
// _typeChanged will update _computedProperties, _isGrpcApi, and _deepResolvedType
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// If renderReadOnly changed and we have an object, recalculate properties
|
|
384
|
+
// This is needed because _filterReadOnlyProperties depends on this.renderReadOnly
|
|
385
|
+
if (changedProperties.has('renderReadOnly') && this._resolvedType && this.isObject) {
|
|
386
|
+
this._computedProperties = this._computeProperties(this._resolvedType);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// If noMainExample changed, recalculate whether to render examples
|
|
390
|
+
if (changedProperties.has('noMainExample') && this._resolvedType) {
|
|
391
|
+
this._showExamples = !this.noMainExample && (
|
|
392
|
+
this.renderMediaSelector ||
|
|
393
|
+
this.isObject ||
|
|
394
|
+
this._renderMainExample
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
348
399
|
/**
|
|
349
400
|
* Called when resolved type or amf changed.
|
|
350
401
|
* Creates a debouncer to compute UI values so it's independent of
|
|
@@ -455,6 +506,32 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
455
506
|
this.isAnd = isAnd;
|
|
456
507
|
this.isOneOf = isOneOf;
|
|
457
508
|
this.isAnyOf = isAnyOf;
|
|
509
|
+
|
|
510
|
+
// Compute properties for objects - this needs to be reactive
|
|
511
|
+
if (isObject) {
|
|
512
|
+
this._computedProperties = this._computeProperties(type);
|
|
513
|
+
} else {
|
|
514
|
+
this._computedProperties = undefined;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Always deep resolve for examples (resolves link-target references for gRPC and similar)
|
|
518
|
+
// This is cheap if there are no link-targets
|
|
519
|
+
if (type) {
|
|
520
|
+
this._resolvedExampleType = this._deepResolveType(type);
|
|
521
|
+
} else {
|
|
522
|
+
this._resolvedExampleType = type;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Determine if we should show the examples section
|
|
526
|
+
// Priority: noMainExample (hide) > renderMediaSelector (show) > isObject (show) > _renderMainExample
|
|
527
|
+
this._showExamples = !this.noMainExample && (
|
|
528
|
+
this.renderMediaSelector || // Need to show the section for the media type selector
|
|
529
|
+
isObject || // Objects can generate examples automatically
|
|
530
|
+
this._renderMainExample // Has explicit examples
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
// Effective media type - use 'application/json' as default for objects without mediaType
|
|
534
|
+
this._exampleMediaType = this.mediaType || (isObject ? 'application/json' : undefined);
|
|
458
535
|
}
|
|
459
536
|
|
|
460
537
|
/**
|
|
@@ -526,18 +603,11 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
526
603
|
if (Array.isArray(item)) {
|
|
527
604
|
[item] = item;
|
|
528
605
|
}
|
|
606
|
+
// For array types in unions, return the array itself instead of unwrapping to items
|
|
607
|
+
// This preserves the "array of" indicator in the UI
|
|
529
608
|
if (this._hasType(item, this.ns.aml.vocabularies.shapes.ArrayShape)) {
|
|
530
609
|
item = this._resolve(item);
|
|
531
|
-
|
|
532
|
-
const items = this._ensureArray(item[itemsKey]);
|
|
533
|
-
if (items && items.length === 1) {
|
|
534
|
-
let result = items[0];
|
|
535
|
-
if (Array.isArray(result)) {
|
|
536
|
-
[result] = result;
|
|
537
|
-
}
|
|
538
|
-
result = this._resolve(result);
|
|
539
|
-
return result;
|
|
540
|
-
}
|
|
610
|
+
return item;
|
|
541
611
|
}
|
|
542
612
|
if (Array.isArray(item)) {
|
|
543
613
|
[item] = item;
|
|
@@ -546,6 +616,91 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
546
616
|
return this._resolve(item);
|
|
547
617
|
}
|
|
548
618
|
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Deeply resolves link-target references in a type for example generation.
|
|
622
|
+
* This ensures that nested objects show their full structure in examples.
|
|
623
|
+
* This is needed for APIs like gRPC where nested types use link-target references.
|
|
624
|
+
*
|
|
625
|
+
* @param {Object} type The type to resolve
|
|
626
|
+
* @return {Object} The deeply resolved type
|
|
627
|
+
*/
|
|
628
|
+
_deepResolveType(type) {
|
|
629
|
+
if (!type || !this.amf) {
|
|
630
|
+
return type;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const resolved = this._resolve(type);
|
|
634
|
+
if (!resolved) {
|
|
635
|
+
return type;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// Get properties
|
|
639
|
+
const propertyKey = this._getAmfKey(this.ns.w3.shacl.property);
|
|
640
|
+
const properties = this._ensureArray(resolved[propertyKey]);
|
|
641
|
+
|
|
642
|
+
if (!properties || !properties.length) {
|
|
643
|
+
return resolved;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Create a new type object with deeply resolved properties
|
|
647
|
+
const deepResolved = { ...resolved };
|
|
648
|
+
const linkTargetKey = this._getAmfKey(this.ns.aml.vocabularies.document.linkTarget);
|
|
649
|
+
const rangeKey = this._getAmfKey(this.ns.raml.vocabularies.shapes.range);
|
|
650
|
+
|
|
651
|
+
// Resolve each property's range
|
|
652
|
+
const resolvedProperties = properties.map(prop => {
|
|
653
|
+
const resolvedProp = this._resolve(prop);
|
|
654
|
+
if (!resolvedProp) {
|
|
655
|
+
return prop;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const range = this._ensureArray(resolvedProp[rangeKey])[0];
|
|
659
|
+
if (!range) {
|
|
660
|
+
return resolvedProp;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// If the range has a link-target, resolve it
|
|
664
|
+
if (range[linkTargetKey]) {
|
|
665
|
+
const linkTargetId = this._ensureArray(range[linkTargetKey])[0];
|
|
666
|
+
if (linkTargetId && linkTargetId['@id']) {
|
|
667
|
+
const targetId = linkTargetId['@id'];
|
|
668
|
+
|
|
669
|
+
// Find the target
|
|
670
|
+
const declares = this._computeDeclares(this.amf);
|
|
671
|
+
let target = declares ? this._findById(declares, targetId) : undefined;
|
|
672
|
+
|
|
673
|
+
if (!target) {
|
|
674
|
+
const references = this._computeReferences(this.amf);
|
|
675
|
+
if (references && references.length) {
|
|
676
|
+
for (let i = 0; i < references.length && !target; i++) {
|
|
677
|
+
const refDeclares = this._computeDeclares(references[i]);
|
|
678
|
+
if (refDeclares) {
|
|
679
|
+
target = this._findById(refDeclares, targetId);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// If target found, replace the range with the resolved target
|
|
686
|
+
if (target) {
|
|
687
|
+
const resolvedTarget = this._resolve(target);
|
|
688
|
+
if (resolvedTarget) {
|
|
689
|
+
const newProp = { ...resolvedProp };
|
|
690
|
+
newProp[rangeKey] = [resolvedTarget];
|
|
691
|
+
return newProp;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return resolvedProp;
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
deepResolved[propertyKey] = resolvedProperties;
|
|
701
|
+
return deepResolved;
|
|
702
|
+
}
|
|
703
|
+
|
|
549
704
|
/**
|
|
550
705
|
* Helper function for the view. Extracts `http://www.w3.org/ns/shacl#property`
|
|
551
706
|
* from the shape model
|
|
@@ -561,16 +716,65 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
561
716
|
return item;
|
|
562
717
|
}
|
|
563
718
|
|
|
719
|
+
// For objects with link-target, we need to find the actual target with properties
|
|
720
|
+
const linkTargetKey = this._getAmfKey(this.ns.aml.vocabularies.document.linkTarget);
|
|
721
|
+
let resolvedItem = item;
|
|
722
|
+
|
|
723
|
+
if (item[linkTargetKey] && this.amf) {
|
|
724
|
+
const linkTargetId = this._ensureArray(item[linkTargetKey])[0];
|
|
725
|
+
if (linkTargetId && linkTargetId['@id']) {
|
|
726
|
+
const targetId = linkTargetId['@id'];
|
|
727
|
+
// Try to find the target in declares
|
|
728
|
+
const declares = this._computeDeclares(this.amf);
|
|
729
|
+
let target = declares ? this._findById(declares, targetId) : undefined;
|
|
730
|
+
|
|
731
|
+
// If not found in declares, search in references
|
|
732
|
+
if (!target) {
|
|
733
|
+
const references = this._computeReferences(this.amf);
|
|
734
|
+
if (references && references.length) {
|
|
735
|
+
for (let i = 0; i < references.length && !target; i++) {
|
|
736
|
+
const refDeclares = this._computeDeclares(references[i]);
|
|
737
|
+
if (refDeclares) {
|
|
738
|
+
target = this._findById(refDeclares, targetId);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// If we found the target and it has properties, use it
|
|
745
|
+
// Don't use the target directly to avoid caching issues
|
|
746
|
+
const propertyKey = this._getAmfKey(this.ns.w3.shacl.property);
|
|
747
|
+
if (target && target[propertyKey]) {
|
|
748
|
+
// Use the target's properties but keep the original item structure
|
|
749
|
+
// This prevents issues with cached __apicResolved flags
|
|
750
|
+
resolvedItem = this._resolve(target);
|
|
751
|
+
// If resolve returned the same object or failed, fallback to standard resolve
|
|
752
|
+
if (!resolvedItem || !resolvedItem[propertyKey]) {
|
|
753
|
+
resolvedItem = this._resolve(item);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Fallback to standard resolve if no link-target or target not found
|
|
760
|
+
if (resolvedItem === item) {
|
|
761
|
+
resolvedItem = this._resolve(item);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (!resolvedItem) {
|
|
765
|
+
return undefined;
|
|
766
|
+
}
|
|
767
|
+
|
|
564
768
|
const propertyKey = this._getAmfKey(this.ns.w3.shacl.property);
|
|
565
|
-
const itemProperties = this._ensureArray(
|
|
769
|
+
const itemProperties = this._ensureArray(resolvedItem[propertyKey]||[])
|
|
566
770
|
const additionalPropertiesKey = this._getAmfKey(this.ns.w3.shacl.additionalPropertiesSchema);
|
|
567
771
|
|
|
568
772
|
// If the item doesn't have additional properties, filter the read-only properties and return
|
|
569
|
-
if (!
|
|
773
|
+
if (!resolvedItem[additionalPropertiesKey]) {
|
|
570
774
|
return this._filterReadOnlyProperties(itemProperties)
|
|
571
775
|
}
|
|
572
776
|
|
|
573
|
-
const additionalPropertiesSchema = this._ensureArray(
|
|
777
|
+
const additionalPropertiesSchema = this._ensureArray(resolvedItem[additionalPropertiesKey])
|
|
574
778
|
|
|
575
779
|
// If the item does have additional properties, ensure they are in an array
|
|
576
780
|
const additionalProperties = this._ensureArray(additionalPropertiesSchema[0][propertyKey] || additionalPropertiesSchema[0])
|
|
@@ -688,7 +892,7 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
688
892
|
* @return {TemplateResult[]|string} Templates for object properties
|
|
689
893
|
*/
|
|
690
894
|
_objectTemplate() {
|
|
691
|
-
const items = this.
|
|
895
|
+
const items = this._computedProperties;
|
|
692
896
|
if (!items || !items.length) {
|
|
693
897
|
return '';
|
|
694
898
|
}
|
|
@@ -924,8 +1128,16 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
924
1128
|
parts +=
|
|
925
1129
|
'code-content-action-button-active, code-wrapper, example-code-wrapper, markdown-html';
|
|
926
1130
|
const mediaTypes = (this.mediaTypes || []);
|
|
1131
|
+
// Use cached values if available, otherwise fallback to computed values
|
|
1132
|
+
const shouldRenderExamples = this._showExamples !== undefined
|
|
1133
|
+
? this._showExamples
|
|
1134
|
+
: this._renderMainExample;
|
|
1135
|
+
const exampleMediaType = this._exampleMediaType !== undefined
|
|
1136
|
+
? this._exampleMediaType
|
|
1137
|
+
: (this.mediaType || (this.isObject ? 'application/json' : undefined));
|
|
1138
|
+
|
|
927
1139
|
return html`<style>${this.styles}</style>
|
|
928
|
-
|
|
1140
|
+
${shouldRenderExamples ? html`<section class="examples">
|
|
929
1141
|
${this.shouldRenderMediaSelector
|
|
930
1142
|
? html`<div class="media-type-selector">
|
|
931
1143
|
<span>Media type:</span>
|
|
@@ -950,18 +1162,18 @@ export class ApiTypeDocument extends PropertyDocumentMixin(LitElement) {
|
|
|
950
1162
|
<api-resource-example-document
|
|
951
1163
|
.amf="${this.amf}"
|
|
952
1164
|
.payloadId="${this.selectedBodyId}"
|
|
953
|
-
.examples="${this._resolvedType}"
|
|
954
|
-
.mediaType="${
|
|
1165
|
+
.examples="${this._resolvedExampleType || this._resolvedType}"
|
|
1166
|
+
.mediaType="${exampleMediaType}"
|
|
955
1167
|
.typeName="${this.parentTypeName}"
|
|
956
1168
|
@has-examples-changed="${this._hasExamplesHandler}"
|
|
957
1169
|
?noauto="${!!this.isScalar}"
|
|
958
1170
|
?noactions="${this.noExamplesActions}"
|
|
959
|
-
?rawOnly="${!
|
|
1171
|
+
?rawOnly="${!exampleMediaType}"
|
|
960
1172
|
?compatibility="${this.compatibility}"
|
|
961
1173
|
exportParts="${parts}"
|
|
962
1174
|
?renderReadOnly="${this.renderReadOnly}"
|
|
963
1175
|
></api-resource-example-document>
|
|
964
|
-
</section
|
|
1176
|
+
</section>` : ''}
|
|
965
1177
|
|
|
966
1178
|
${this.isObject ? this._objectTemplate() : ''}
|
|
967
1179
|
${this.isArray ? this._arrayTemplate() : ''}
|