Skip to content

Commit 1a2d000

Browse files
authored
Merge pull request #74 from advanced-rest-client/fix/W-19596014/additional-properties-unknown-type-4.4.28
W-19596014 fix: handle null properties in _jsonExampleFromProperties method to p…
2 parents 5d782d8 + 7455123 commit 1a2d000

File tree

4 files changed

+168
-3
lines changed

4 files changed

+168
-3
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@api-components/api-example-generator",
33
"description": "Examples generator from AMF model",
4-
"version": "4.4.34",
4+
"version": "4.4.35",
55
"license": "Apache-2.0",
66
"main": "index.js",
77
"module": "index.js",

src/ExampleGenerator.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,9 @@ export class ExampleGenerator extends AmfHelperMixin(Object) {
13961396
*/
13971397
_jsonExampleFromProperties(properties) {
13981398
const result = {};
1399+
if (!properties) {
1400+
return result;
1401+
}
13991402
for (let i = 0, len = properties.length; i < len; i++) {
14001403
const property = properties[i];
14011404
const name = this._getValue(property, this.ns.w3.shacl.name);
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { fixture, assert, html } from '@open-wc/testing';
2+
import { AmfLoader } from './amf-loader.js';
3+
import '../api-example-generator.js';
4+
5+
describe('Additional Properties Fix', () => {
6+
async function basicFixture(amf) {
7+
return (await fixture(html`<api-example-generator
8+
.amf="${amf}"></api-example-generator>`));
9+
}
10+
11+
const apiFile = 'v4_0_0_api_specs';
12+
13+
[
14+
['json+ld data model', false],
15+
['Compact data model', true]
16+
].forEach(([label, compact]) => {
17+
describe(label, () => {
18+
let element;
19+
let amf;
20+
21+
before(async () => {
22+
amf = await AmfLoader.load(compact, apiFile);
23+
});
24+
25+
beforeEach(async () => {
26+
element = await basicFixture(amf);
27+
});
28+
29+
it('should handle additionalProperties without crashing', () => {
30+
// This test reproduces the bug where additionalProperties caused
31+
// a crash due to properties being null/undefined
32+
33+
// Look for a payload that uses ForceConfig schema which has additionalProperties: true
34+
const payloads = AmfLoader.lookupPayload(
35+
amf,
36+
'/v4.0.0/messages',
37+
'post'
38+
);
39+
40+
// This should not throw an error and should generate a valid example
41+
const result = element.generatePayloadsExamples(
42+
payloads,
43+
'application/json'
44+
);
45+
46+
assert.typeOf(result, 'array', 'Should return an array of examples');
47+
assert.isAbove(result.length, 0, 'Should have at least one example');
48+
49+
const example = result[0];
50+
assert.typeOf(example.value, 'string', 'Example should have a string value');
51+
assert.isFalse(example.hasRaw, 'Should not be a raw example');
52+
assert.isFalse(example.hasTitle, 'Should not have a title');
53+
assert.isFalse(example.hasUnion, 'Should not be a union');
54+
55+
// Verify it's valid JSON
56+
assert.doesNotThrow(() => {
57+
JSON.parse(example.value);
58+
}, 'Generated example should be valid JSON');
59+
});
60+
61+
it('should generate examples for schemas with additionalProperties: true', () => {
62+
// Test specifically for schemas that have additionalProperties: true
63+
// This tests the fix in _computeJsonObjectValue method
64+
65+
const payloads = AmfLoader.lookupPayload(
66+
amf,
67+
'/v4.0.0/messages',
68+
'post'
69+
);
70+
71+
const result = element.generatePayloadsExamples(
72+
payloads,
73+
'application/json'
74+
);
75+
76+
assert.typeOf(result, 'array');
77+
const example = result[0];
78+
79+
// The example should be generated successfully without "Unknown type"
80+
assert.typeOf(example.value, 'string');
81+
assert.notInclude(example.value.toLowerCase(), 'unknown', 'Should not contain "unknown" type');
82+
83+
// Parse the JSON to verify structure
84+
const parsedExample = JSON.parse(example.value);
85+
assert.typeOf(parsedExample, 'object', 'Should generate a valid JSON object');
86+
});
87+
88+
it('should handle _jsonExampleFromProperties with null properties gracefully', () => {
89+
// This test verifies the specific fix: adding null check in _jsonExampleFromProperties
90+
91+
// Test the method directly with null properties
92+
const result = element._jsonExampleFromProperties(null);
93+
assert.typeOf(result, 'object', 'Should return an empty object when properties is null');
94+
assert.deepEqual(result, {}, 'Should return empty object for null properties');
95+
96+
// Test with undefined properties
97+
const result2 = element._jsonExampleFromProperties(undefined);
98+
assert.typeOf(result2, 'object', 'Should return an empty object when properties is undefined');
99+
assert.deepEqual(result2, {}, 'Should return empty object for undefined properties');
100+
101+
// Test with empty array (should work as before)
102+
const result3 = element._jsonExampleFromProperties([]);
103+
assert.typeOf(result3, 'object', 'Should return an empty object for empty array');
104+
assert.deepEqual(result3, {}, 'Should return empty object for empty properties array');
105+
});
106+
107+
it('should handle _computeJsonObjectValue with additionalProperties correctly', () => {
108+
// Test the _computeJsonObjectValue method directly to ensure it handles
109+
// additionalProperties scenarios without crashing
110+
111+
// Create a mock range object similar to what would be generated for additionalProperties
112+
const mockRange = {
113+
[element._getAmfKey(element.ns.w3.shacl.property)]: null, // No regular properties
114+
[element._getAmfKey(element.ns.w3.shacl.additionalPropertiesSchema)]: [
115+
{
116+
[element._getAmfKey(element.ns.w3.shacl.property)]: null // additionalProperties with null properties
117+
}
118+
]
119+
};
120+
121+
// This should not crash and should return an empty object
122+
const result = element._computeJsonObjectValue(mockRange);
123+
assert.typeOf(result, 'object', 'Should return an object');
124+
assert.deepEqual(result, {}, 'Should return empty object when no properties can be processed');
125+
});
126+
127+
it('regression test: should work with version 4.4.27 behavior and beyond', () => {
128+
// This is a regression test to ensure the fix maintains compatibility
129+
// with the behavior that worked in version 4.4.27
130+
131+
const payloads = AmfLoader.lookupPayload(
132+
amf,
133+
'/v4.0.0/messages',
134+
'post'
135+
);
136+
137+
// Before the fix (4.4.28+), this would throw an error or return "Unknown type"
138+
// After the fix, it should work correctly
139+
assert.doesNotThrow(() => {
140+
const result = element.generatePayloadsExamples(
141+
payloads,
142+
'application/json'
143+
);
144+
145+
assert.typeOf(result, 'array', 'Should successfully generate examples');
146+
147+
if (result && result.length > 0) {
148+
const example = result[0];
149+
assert.typeOf(example.value, 'string', 'Should have a valid example value');
150+
151+
// Ensure it doesn't contain "Unknown type" which was the reported issue
152+
assert.notInclude(
153+
example.value,
154+
'unknown-type',
155+
'Should not generate unknown-type in examples'
156+
);
157+
}
158+
}, 'Should not throw errors when processing additionalProperties');
159+
});
160+
});
161+
});
162+
});

0 commit comments

Comments
 (0)