Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions lib/markdownBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -847,11 +847,13 @@ export default function build({
/**
* Generates the properties section for a schema
* @param {*} schema
* @param {*} slugger
* @param {number} level - Base level for this schema
*/
function makeproperties(schema, slugger) {
function makeproperties(schema, slugger, level = 1) {
if (schema[keyword`properties`] || schema[keyword`patternProperties`] || schema[keyword`additionalProperties`]) {
return [
heading(1, text(i18n`${simpletitle(schema)} Properties`)),
heading(level, text(i18n`${simpletitle(schema)} Properties`)),
makeproptable(
schema[keyword`properties`],
schema[keyword`patternProperties`],
Expand All @@ -864,25 +866,49 @@ export default function build({
schema[keyword`patternProperties`],
schema[keyword`additionalProperties`],
schema[keyword`required`],
1,
level,
),
];
}
return [];
}

/**
* Calculates the appropriate base header level for a schema based on its position
* in the schema hierarchy. Schemas within composition constructs (allOf/anyOf/oneOf)
* that are nested within properties should use higher header levels to maintain
* proper document hierarchy.
*
* @param {string} pointer - JSON pointer path to the schema
* @returns {number} The base header level (1-6)
*/
function calculateBaseLevel(pointer) {
// Only adjust level for schemas within composition constructs
const isCompositionSchema = /\/(allOf|anyOf|oneOf)\//.test(pointer);
if (!isCompositionSchema) return 1;

// Calculate property nesting depth by counting /properties/ segments
const propertyDepth = (pointer.match(/\/properties\//g) || []).length;
return propertyDepth > 0 ? propertyDepth : 1;
}

console.log('generating markdown');
return (schemas) => foldl(schemas, {}, (pv, schema) => {
const slugger = new GhSlugger();

// Calculate the appropriate base level for this schema based on its position
// in the hierarchy (composition constructs within properties need higher levels)
const baseLevel = calculateBaseLevel(schema[s.pointer]);

// eslint-disable-next-line no-param-reassign
pv[schema[s.slug]] = root([
// todo add more elements
...makeheader(schema),
...maketypesection(schema, 1),
...makeconstraintssection(schema, 1),
...makedefault(schema, 1),
...makeexamples(schema, 1),
...makeproperties(schema, slugger),
...maketypesection(schema, baseLevel),
...makeconstraintssection(schema, baseLevel),
...makedefault(schema, baseLevel),
...makeexamples(schema, baseLevel),
...makeproperties(schema, slugger, baseLevel),
...makedefinitions(schema, slugger),
]);
return pv;
Expand Down
10 changes: 0 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 63 additions & 0 deletions test/allof-anyof-hierarchy.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-env mocha */
/* eslint-disable no-unused-expressions */
import { assertMarkdown, traverseSchemas } from './testUtils.js';
import build from '../lib/markdownBuilder.js';

describe('Testing Markdown Builder: allOf/anyOf header hierarchy', () => {
let results;

before(async () => {
const schemas = await traverseSchemas('allof-anyof-hierarchy');
const builder = build({ header: true });
results = builder(schemas);
});

it('allOf item schemas should use higher base level for proper hierarchy', () => {
// The allOf[1] item is a composition schema within the docker property
// It should use baseLevel = 2 (property depth) instead of 1
const allof1Slug = 'schema-properties-tests-configuration-properties-docker-tests-allof-1';
assertMarkdown(results[allof1Slug])
.matches(/^## 1 Properties$/m) // Properties heading at level 2 (baseLevel)
.matches(/^### preTestSteps/m) // Property detail at level 3 (baseLevel + 1)
.matches(/^#### preTestSteps Type$/m); // Type section at level 4 (baseLevel + 1 + 1)
});

it('anyOf item schemas should use higher base level for proper hierarchy', () => {
// The anyOf[1] item within allOf[0] is a composition schema
// It should also use baseLevel = 2 (property depth) instead of 1
const anyof1Slug = 'schema-properties-tests-configuration-properties-docker-tests-allof-test-options-anyof-steplist';
assertMarkdown(results[anyof1Slug])
.matches(/^## 1 Properties$/m) // Properties heading at level 2 (baseLevel)
.matches(/^### testSteps/m) // Property detail at level 3 (baseLevel + 1)
.matches(/^#### testSteps Type$/m); // Type section at level 4 (baseLevel + 1 + 1)
});

it('oneOf item schemas should use higher base level for proper hierarchy', () => {
// The oneOf[0] item within kubernetes property is a composition schema
// It should use baseLevel = 2 (property depth) instead of 1
const oneof0Slug = 'schema-properties-tests-configuration-properties-kubernetes-tests-oneof-helm';
assertMarkdown(results[oneof0Slug])
.matches(/^## 0 Properties$/m) // Properties heading at level 2 (baseLevel)
.matches(/^### helmChart/m) // Property detail at level 3 (baseLevel + 1)
.matches(/^#### helmChart Type$/m); // Type section at level 4 (baseLevel + 1 + 1)
});

it('regular property schemas should still use baseLevel 1', () => {
// Regular property schemas (not in composition constructs) should still use level 1
const dockerSlug = 'schema-properties-tests-configuration-properties-docker-tests';
assertMarkdown(results[dockerSlug])
.matches(/^# Docker Tests Schema$/m) // Schema header at level 1
.matches(/^## docker Type$/m); // Type section at level 2 (baseLevel 1 + 1)
});
});
93 changes: 93 additions & 0 deletions test/fixtures/allof-anyof-hierarchy/schema.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Example Schema",
"type": "object",
"properties": {
"tests": {
"title": "Tests Configuration",
"description": "Configure test execution",
"type": "object",
"properties": {
"docker": {
"title": "Docker Tests",
"description": "Run Docker-based tests",
"type": "object",
"allOf": [
{
"title": "test-options",
"description": "Test execution options",
"anyOf": [
{
"title": "script",
"properties": {
"testScript": {
"title": "Test Script",
"description": "Script to execute for testing",
"type": "string"
}
},
"required": ["testScript"]
},
{
"title": "stepList",
"properties": {
"testSteps": {
"title": "Test Steps",
"description": "List of test steps to execute",
"type": "array",
"items": {
"type": "object"
}
}
},
"required": ["testSteps"]
}
]
},
{
"properties": {
"preTestSteps": {
"title": "Pre-test Steps",
"description": "Steps to execute before running tests",
"type": "array",
"items": {
"type": "object"
}
}
}
}
]
},
"kubernetes": {
"title": "Kubernetes Tests",
"description": "Run Kubernetes-based tests",
"type": "object",
"oneOf": [
{
"title": "helm",
"properties": {
"helmChart": {
"title": "Helm Chart",
"description": "Helm chart to deploy for testing",
"type": "string"
}
},
"required": ["helmChart"]
},
{
"title": "manifest",
"properties": {
"manifestFile": {
"title": "Manifest File",
"description": "Kubernetes manifest file to apply",
"type": "string"
}
},
"required": ["manifestFile"]
}
]
}
}
}
}
}