From 14361bc0490734208e39aacb0051868d1c4ee0bd Mon Sep 17 00:00:00 2001 From: Alex Perez Date: Mon, 22 Sep 2025 11:36:45 -0300 Subject: [PATCH 1/3] fix: handle null properties in _jsonExampleFromProperties method to prevent errors --- src/ExampleGenerator.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ExampleGenerator.js b/src/ExampleGenerator.js index 9bb0852..3b49bcd 100644 --- a/src/ExampleGenerator.js +++ b/src/ExampleGenerator.js @@ -1396,6 +1396,9 @@ export class ExampleGenerator extends AmfHelperMixin(Object) { */ _jsonExampleFromProperties(properties) { const result = {}; + if (!properties) { + return result; + } for (let i = 0, len = properties.length; i < len; i++) { const property = properties[i]; const name = this._getValue(property, this.ns.w3.shacl.name); From 9975d1c01a49e7d5ffe5a9aa5fcf37b2d3229a49 Mon Sep 17 00:00:00 2001 From: Alex Perez Date: Mon, 22 Sep 2025 11:41:07 -0300 Subject: [PATCH 2/3] 4.4.35 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5363692..51437ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@api-components/api-example-generator", - "version": "4.4.34", + "version": "4.4.35", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@api-components/api-example-generator", - "version": "4.4.34", + "version": "4.4.35", "license": "Apache-2.0", "dependencies": { "@api-components/amf-helper-mixin": "^4.5.24", diff --git a/package.json b/package.json index 28d4258..0156a27 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@api-components/api-example-generator", "description": "Examples generator from AMF model", - "version": "4.4.34", + "version": "4.4.35", "license": "Apache-2.0", "main": "index.js", "module": "index.js", From 745512394155f290425caead387481e8c9c3dcfd Mon Sep 17 00:00:00 2001 From: Alex Perez Date: Mon, 22 Sep 2025 11:45:48 -0300 Subject: [PATCH 3/3] test: add unit tests for handling additionalProperties in API examples - Introduced tests to ensure the application does not crash when handling additionalProperties, including scenarios with null and undefined properties. - Verified that examples are generated correctly without "unknown" types and that the methods handle edge cases gracefully. - Added regression tests to maintain compatibility with previous versions. --- test/additional-properties-fix.test.js | 162 +++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 test/additional-properties-fix.test.js diff --git a/test/additional-properties-fix.test.js b/test/additional-properties-fix.test.js new file mode 100644 index 0000000..ed1a8ed --- /dev/null +++ b/test/additional-properties-fix.test.js @@ -0,0 +1,162 @@ +import { fixture, assert, html } from '@open-wc/testing'; +import { AmfLoader } from './amf-loader.js'; +import '../api-example-generator.js'; + +describe('Additional Properties Fix', () => { + async function basicFixture(amf) { + return (await fixture(html``)); + } + + const apiFile = 'v4_0_0_api_specs'; + + [ + ['json+ld data model', false], + ['Compact data model', true] + ].forEach(([label, compact]) => { + describe(label, () => { + let element; + let amf; + + before(async () => { + amf = await AmfLoader.load(compact, apiFile); + }); + + beforeEach(async () => { + element = await basicFixture(amf); + }); + + it('should handle additionalProperties without crashing', () => { + // This test reproduces the bug where additionalProperties caused + // a crash due to properties being null/undefined + + // Look for a payload that uses ForceConfig schema which has additionalProperties: true + const payloads = AmfLoader.lookupPayload( + amf, + '/v4.0.0/messages', + 'post' + ); + + // This should not throw an error and should generate a valid example + const result = element.generatePayloadsExamples( + payloads, + 'application/json' + ); + + assert.typeOf(result, 'array', 'Should return an array of examples'); + assert.isAbove(result.length, 0, 'Should have at least one example'); + + const example = result[0]; + assert.typeOf(example.value, 'string', 'Example should have a string value'); + assert.isFalse(example.hasRaw, 'Should not be a raw example'); + assert.isFalse(example.hasTitle, 'Should not have a title'); + assert.isFalse(example.hasUnion, 'Should not be a union'); + + // Verify it's valid JSON + assert.doesNotThrow(() => { + JSON.parse(example.value); + }, 'Generated example should be valid JSON'); + }); + + it('should generate examples for schemas with additionalProperties: true', () => { + // Test specifically for schemas that have additionalProperties: true + // This tests the fix in _computeJsonObjectValue method + + const payloads = AmfLoader.lookupPayload( + amf, + '/v4.0.0/messages', + 'post' + ); + + const result = element.generatePayloadsExamples( + payloads, + 'application/json' + ); + + assert.typeOf(result, 'array'); + const example = result[0]; + + // The example should be generated successfully without "Unknown type" + assert.typeOf(example.value, 'string'); + assert.notInclude(example.value.toLowerCase(), 'unknown', 'Should not contain "unknown" type'); + + // Parse the JSON to verify structure + const parsedExample = JSON.parse(example.value); + assert.typeOf(parsedExample, 'object', 'Should generate a valid JSON object'); + }); + + it('should handle _jsonExampleFromProperties with null properties gracefully', () => { + // This test verifies the specific fix: adding null check in _jsonExampleFromProperties + + // Test the method directly with null properties + const result = element._jsonExampleFromProperties(null); + assert.typeOf(result, 'object', 'Should return an empty object when properties is null'); + assert.deepEqual(result, {}, 'Should return empty object for null properties'); + + // Test with undefined properties + const result2 = element._jsonExampleFromProperties(undefined); + assert.typeOf(result2, 'object', 'Should return an empty object when properties is undefined'); + assert.deepEqual(result2, {}, 'Should return empty object for undefined properties'); + + // Test with empty array (should work as before) + const result3 = element._jsonExampleFromProperties([]); + assert.typeOf(result3, 'object', 'Should return an empty object for empty array'); + assert.deepEqual(result3, {}, 'Should return empty object for empty properties array'); + }); + + it('should handle _computeJsonObjectValue with additionalProperties correctly', () => { + // Test the _computeJsonObjectValue method directly to ensure it handles + // additionalProperties scenarios without crashing + + // Create a mock range object similar to what would be generated for additionalProperties + const mockRange = { + [element._getAmfKey(element.ns.w3.shacl.property)]: null, // No regular properties + [element._getAmfKey(element.ns.w3.shacl.additionalPropertiesSchema)]: [ + { + [element._getAmfKey(element.ns.w3.shacl.property)]: null // additionalProperties with null properties + } + ] + }; + + // This should not crash and should return an empty object + const result = element._computeJsonObjectValue(mockRange); + assert.typeOf(result, 'object', 'Should return an object'); + assert.deepEqual(result, {}, 'Should return empty object when no properties can be processed'); + }); + + it('regression test: should work with version 4.4.27 behavior and beyond', () => { + // This is a regression test to ensure the fix maintains compatibility + // with the behavior that worked in version 4.4.27 + + const payloads = AmfLoader.lookupPayload( + amf, + '/v4.0.0/messages', + 'post' + ); + + // Before the fix (4.4.28+), this would throw an error or return "Unknown type" + // After the fix, it should work correctly + assert.doesNotThrow(() => { + const result = element.generatePayloadsExamples( + payloads, + 'application/json' + ); + + assert.typeOf(result, 'array', 'Should successfully generate examples'); + + if (result && result.length > 0) { + const example = result[0]; + assert.typeOf(example.value, 'string', 'Should have a valid example value'); + + // Ensure it doesn't contain "Unknown type" which was the reported issue + assert.notInclude( + example.value, + 'unknown-type', + 'Should not generate unknown-type in examples' + ); + } + }, 'Should not throw errors when processing additionalProperties'); + }); + }); + }); +});