docusaurus-plugin-generate-schema-docs 1.3.0 → 1.5.0
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/README.md +28 -0
- package/__tests__/ExampleDataLayer.test.js +13 -0
- package/__tests__/__fixtures__/static/schemas/anchor/parent-event-anchor.json +29 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/add-to-cart-event.json +45 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/choice-event.json +78 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/complex-event.json +193 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/components/dataLayer.json +56 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/components/product.json +126 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/purchase-event.json +73 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/root-any-of-event.json +40 -0
- package/__tests__/__fixtures__/validateSchemas/complex-validation/events/root-choice-event.json +54 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +79 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +8 -2
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +20 -5
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +8 -2
- package/__tests__/components/PropertiesTable.test.js +34 -1
- package/__tests__/components/PropertyRow.test.js +5 -5
- package/__tests__/generateEventDocs.anchor.test.js +71 -0
- package/__tests__/helpers/example-helper.test.js +71 -0
- package/__tests__/helpers/getConstraints.test.js +72 -27
- package/__tests__/helpers/schemaToTableData.test.js +28 -33
- package/__tests__/helpers/validator.test.js +115 -0
- package/__tests__/validateSchemas-integration.test.js +29 -0
- package/__tests__/validateSchemas.test.js +18 -102
- package/components/ExampleDataLayer.js +14 -5
- package/components/PropertiesTable.js +22 -7
- package/components/PropertiesTable.module.css +27 -0
- package/components/PropertyRow.js +24 -25
- package/components/SchemaRows.css +6 -0
- package/components/SchemaViewer.js +2 -2
- package/components/WordWrapButton.js +31 -0
- package/components/wordWrapButton.module.css +8 -0
- package/generateEventDocs.js +2 -1
- package/helpers/buildExampleFromSchema.js +49 -52
- package/helpers/example-helper.js +41 -0
- package/helpers/file-system.js +1 -2
- package/helpers/getConstraints.js +1 -0
- package/helpers/schema-doc-template.js +8 -1
- package/helpers/schema-processing.js +2 -15
- package/helpers/schemaToExamples.js +46 -74
- package/helpers/schemaToTableData.js +4 -3
- package/helpers/validator.js +108 -0
- package/index.js +18 -9
- package/package.json +1 -2
- package/validateSchemas.js +70 -53
- package/__tests__/helpers/loadSchema.test.js +0 -20
- package/helpers/loadSchema.js +0 -11
|
@@ -3,25 +3,15 @@ import clsx from 'clsx';
|
|
|
3
3
|
import CodeBlock from '@theme/CodeBlock';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Formats
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param {any} example The example to format.
|
|
10
|
-
* @returns {string}
|
|
6
|
+
* Formats a single example value into a string for display in a CodeBlock.
|
|
7
|
+
* @param {any} singleEx The example to format.
|
|
8
|
+
* @returns {string|null}
|
|
11
9
|
*/
|
|
12
|
-
const formatExample = (
|
|
13
|
-
if (typeof
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
typeof ex === 'object' ? JSON.stringify(ex, null, 2) : String(ex),
|
|
18
|
-
)
|
|
19
|
-
.join('\n');
|
|
20
|
-
}
|
|
21
|
-
if (typeof example === 'object') {
|
|
22
|
-
return JSON.stringify(example, null, 2);
|
|
23
|
-
}
|
|
24
|
-
return String(example);
|
|
10
|
+
const formatExample = (singleEx) => {
|
|
11
|
+
if (typeof singleEx === 'undefined' || singleEx === null) return null;
|
|
12
|
+
if (typeof singleEx === 'object') return JSON.stringify(singleEx, null, 2);
|
|
13
|
+
if (typeof singleEx === 'string') return JSON.stringify(singleEx);
|
|
14
|
+
return String(singleEx);
|
|
25
15
|
};
|
|
26
16
|
|
|
27
17
|
/**
|
|
@@ -47,7 +37,7 @@ export default function PropertyRow({ row, isLastInGroup }) {
|
|
|
47
37
|
required,
|
|
48
38
|
propertyType,
|
|
49
39
|
description,
|
|
50
|
-
|
|
40
|
+
examples,
|
|
51
41
|
constraints,
|
|
52
42
|
hasChildren,
|
|
53
43
|
containerType,
|
|
@@ -65,7 +55,6 @@ export default function PropertyRow({ row, isLastInGroup }) {
|
|
|
65
55
|
const [firstConstraint, ...remainingConstraints] = hasConstraints
|
|
66
56
|
? constraints
|
|
67
57
|
: [null];
|
|
68
|
-
const formattedExample = formatExample(example);
|
|
69
58
|
|
|
70
59
|
// Generate background gradients for:
|
|
71
60
|
// 1. Parent-to-child line (from 50% down) - for elements with children
|
|
@@ -154,11 +143,21 @@ export default function PropertyRow({ row, isLastInGroup }) {
|
|
|
154
143
|
</td>
|
|
155
144
|
|
|
156
145
|
<td rowSpan={rowSpan}>
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
146
|
+
{examples &&
|
|
147
|
+
examples.map((ex, i) => {
|
|
148
|
+
const formatted = formatExample(ex);
|
|
149
|
+
return (
|
|
150
|
+
formatted && (
|
|
151
|
+
<CodeBlock
|
|
152
|
+
key={i}
|
|
153
|
+
language="json"
|
|
154
|
+
className="schema-examples"
|
|
155
|
+
>
|
|
156
|
+
{formatted}
|
|
157
|
+
</CodeBlock>
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
162
161
|
</td>
|
|
163
162
|
<td rowSpan={rowSpan}>{description || ''}</td>
|
|
164
163
|
</tr>
|
|
@@ -3,7 +3,7 @@ import Heading from '@theme/Heading';
|
|
|
3
3
|
import ExampleDataLayer from './ExampleDataLayer';
|
|
4
4
|
import PropertiesTable from './PropertiesTable';
|
|
5
5
|
|
|
6
|
-
export default function SchemaViewer({ schema }) {
|
|
6
|
+
export default function SchemaViewer({ schema, dataLayerName }) {
|
|
7
7
|
const hasOneOfAnyOf =
|
|
8
8
|
schema.oneOf ||
|
|
9
9
|
schema.anyOf ||
|
|
@@ -18,7 +18,7 @@ export default function SchemaViewer({ schema }) {
|
|
|
18
18
|
return (
|
|
19
19
|
<div>
|
|
20
20
|
<Heading as="h2">{exampleTitle}</Heading>
|
|
21
|
-
<ExampleDataLayer schema={schema} />
|
|
21
|
+
<ExampleDataLayer schema={schema} dataLayerName={dataLayerName} />
|
|
22
22
|
|
|
23
23
|
<Heading as="h2">Event Properties</Heading>
|
|
24
24
|
<PropertiesTable schema={schema} />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import styles from './wordWrapButton.module.css';
|
|
4
|
+
|
|
5
|
+
export default function WordWrapButton({ className, onClick, isEnabled }) {
|
|
6
|
+
const title = 'Toggle word wrap';
|
|
7
|
+
return (
|
|
8
|
+
<button
|
|
9
|
+
type="button"
|
|
10
|
+
onClick={onClick}
|
|
11
|
+
className={clsx(
|
|
12
|
+
'clean-btn',
|
|
13
|
+
className,
|
|
14
|
+
isEnabled && styles.wordWrapButtonEnabled,
|
|
15
|
+
)}
|
|
16
|
+
aria-label={title}
|
|
17
|
+
title={title}
|
|
18
|
+
>
|
|
19
|
+
<svg
|
|
20
|
+
className={styles.wordWrapButtonIcon}
|
|
21
|
+
viewBox="0 0 24 24"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
>
|
|
24
|
+
<path
|
|
25
|
+
fill="currentColor"
|
|
26
|
+
d="M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"
|
|
27
|
+
/>
|
|
28
|
+
</svg>
|
|
29
|
+
</button>
|
|
30
|
+
);
|
|
31
|
+
}
|
package/generateEventDocs.js
CHANGED
|
@@ -15,7 +15,7 @@ async function generateAndWriteDoc(
|
|
|
15
15
|
options,
|
|
16
16
|
alreadyMergedSchema = null,
|
|
17
17
|
) {
|
|
18
|
-
const { organizationName, projectName, siteDir } = options;
|
|
18
|
+
const { organizationName, projectName, siteDir, dataLayerName } = options;
|
|
19
19
|
const baseEditUrl = `https://github.com/${organizationName}/${projectName}/edit/main`;
|
|
20
20
|
const PARTIALS_DIR = path.join(siteDir, 'docs/partials');
|
|
21
21
|
|
|
@@ -53,6 +53,7 @@ async function generateAndWriteDoc(
|
|
|
53
53
|
bottomPartialImport,
|
|
54
54
|
topPartialComponent,
|
|
55
55
|
bottomPartialComponent,
|
|
56
|
+
dataLayerName,
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
const outputFilename = path.basename(filePath).replace('.json', '.mdx');
|
|
@@ -1,69 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* It prefers explicit examples, consts, or defaults. For choices (`oneOf`/`anyOf`),
|
|
4
|
-
* it defaults to the first option.
|
|
5
|
-
*
|
|
6
|
-
* @param {object} schema The schema to build an example from.
|
|
7
|
-
* @returns {object|undefined} The generated example.
|
|
8
|
-
*/
|
|
9
|
-
const buildExampleFromSchema = (schema) => {
|
|
10
|
-
if (!schema) return undefined;
|
|
1
|
+
import { getSingleExampleValue } from './example-helper';
|
|
2
|
+
import mergeJsonSchema from 'json-schema-merge-allof';
|
|
11
3
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
const getPrimitivePlaceholder = (type) => {
|
|
5
|
+
switch (type) {
|
|
6
|
+
case 'string':
|
|
7
|
+
return '';
|
|
8
|
+
case 'integer':
|
|
9
|
+
case 'number':
|
|
10
|
+
return 0;
|
|
11
|
+
case 'boolean':
|
|
12
|
+
return false;
|
|
13
|
+
default:
|
|
14
|
+
return undefined;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const buildExampleFromSchema = (schema) => {
|
|
19
|
+
if (!schema) {
|
|
20
|
+
return undefined;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
if (schema
|
|
23
|
-
|
|
23
|
+
// For choices, default to the first option and recurse.
|
|
24
|
+
const choiceType = schema.oneOf ? 'oneOf' : schema.anyOf ? 'anyOf' : null;
|
|
25
|
+
if (choiceType && schema[choiceType]?.length > 0) {
|
|
26
|
+
const newSchema = { ...schema };
|
|
27
|
+
const choice = newSchema[choiceType][0];
|
|
28
|
+
delete newSchema[choiceType];
|
|
29
|
+
const merged = mergeJsonSchema({ allOf: [newSchema, choice] });
|
|
30
|
+
return buildExampleFromSchema(merged);
|
|
31
|
+
}
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
// If there's an explicit example, use it.
|
|
34
|
+
const exampleValue = getSingleExampleValue(schema);
|
|
35
|
+
if (typeof exampleValue !== 'undefined') {
|
|
36
|
+
return exampleValue;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
// Determine the type, defaulting to 'object' if properties are present.
|
|
40
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
41
|
+
const inferredType = !type && schema.properties ? 'object' : type;
|
|
42
|
+
|
|
43
|
+
// Build examples based on type.
|
|
44
|
+
if (inferredType === 'object' && schema.properties) {
|
|
45
|
+
const obj = Object.entries(schema.properties).reduce(
|
|
46
|
+
(acc, [key, propSchema]) => {
|
|
34
47
|
const value = buildExampleFromSchema(propSchema);
|
|
35
48
|
if (typeof value !== 'undefined') {
|
|
36
|
-
|
|
49
|
+
acc[key] = value;
|
|
37
50
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return undefined;
|
|
51
|
+
return acc;
|
|
52
|
+
},
|
|
53
|
+
{},
|
|
54
|
+
);
|
|
55
|
+
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// Only return an array if the item schema generates a value.
|
|
50
|
-
return typeof itemValue !== 'undefined' ? [itemValue] : undefined;
|
|
51
|
-
}
|
|
52
|
-
return undefined; // No items defined.
|
|
58
|
+
if (inferredType === 'array' && schema.items) {
|
|
59
|
+
const itemValue = buildExampleFromSchema(schema.items);
|
|
60
|
+
return typeof itemValue !== 'undefined' ? [itemValue] : undefined;
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
switch (type) {
|
|
57
|
-
case 'string':
|
|
58
|
-
return '';
|
|
59
|
-
case 'integer':
|
|
60
|
-
case 'number':
|
|
61
|
-
return 0;
|
|
62
|
-
case 'boolean':
|
|
63
|
-
return false;
|
|
64
|
-
default:
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
63
|
+
return getPrimitivePlaceholder(inferredType);
|
|
67
64
|
};
|
|
68
65
|
|
|
69
66
|
export default buildExampleFromSchema;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function getSingleExampleValue(propSchema) {
|
|
2
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'const')) {
|
|
3
|
+
return propSchema.const;
|
|
4
|
+
}
|
|
5
|
+
if (propSchema.examples?.length > 0) {
|
|
6
|
+
return propSchema.examples[0];
|
|
7
|
+
}
|
|
8
|
+
if (propSchema.example) {
|
|
9
|
+
return propSchema.example;
|
|
10
|
+
}
|
|
11
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'default')) {
|
|
12
|
+
return propSchema.default;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getExamples(propSchema) {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'const')) {
|
|
19
|
+
return [propSchema.const];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const examples = [];
|
|
23
|
+
|
|
24
|
+
if (propSchema.examples) {
|
|
25
|
+
examples.push(...propSchema.examples);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (propSchema.example) {
|
|
29
|
+
if (!examples.includes(propSchema.example)) {
|
|
30
|
+
examples.push(propSchema.example);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'default')) {
|
|
35
|
+
if (!examples.includes(propSchema.default)) {
|
|
36
|
+
examples.push(propSchema.default);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return examples.length > 0 ? examples : undefined;
|
|
41
|
+
}
|
package/helpers/file-system.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import loadSchema from './loadSchema';
|
|
4
3
|
|
|
5
4
|
export function createDir(directory) {
|
|
6
5
|
if (!fs.existsSync(directory)) {
|
|
@@ -15,7 +14,7 @@ export function readSchemas(directory) {
|
|
|
15
14
|
|
|
16
15
|
return files.map((file) => {
|
|
17
16
|
const filePath = path.join(directory, file);
|
|
18
|
-
const schema =
|
|
17
|
+
const schema = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
19
18
|
return {
|
|
20
19
|
fileName: file,
|
|
21
20
|
filePath,
|
|
@@ -15,6 +15,7 @@ const constraintHandlers = {
|
|
|
15
15
|
format: (val) => `format: ${val}`,
|
|
16
16
|
minContains: (val) => `minContains: ${val}`,
|
|
17
17
|
maxContains: (val) => `maxContains: ${val}`,
|
|
18
|
+
default: (val) => `default: ${JSON.stringify(val)}`,
|
|
18
19
|
|
|
19
20
|
// Special-cased constraints
|
|
20
21
|
pattern: (val) => `pattern: /${val}/`,
|
|
@@ -28,7 +28,14 @@ ${schema.description}
|
|
|
28
28
|
|
|
29
29
|
${topPartialComponent}
|
|
30
30
|
|
|
31
|
-
<SchemaViewer
|
|
31
|
+
<SchemaViewer
|
|
32
|
+
schema={${JSON.stringify(mergedSchema)}}
|
|
33
|
+
${
|
|
34
|
+
data.dataLayerName && data.dataLayerName !== 'undefined'
|
|
35
|
+
? ` dataLayerName={'${data.dataLayerName}'}`
|
|
36
|
+
: ''
|
|
37
|
+
}
|
|
38
|
+
/>
|
|
32
39
|
<SchemaJsonViewer schema={${JSON.stringify(schema)}} />
|
|
33
40
|
|
|
34
41
|
${bottomPartialComponent}
|
|
@@ -15,21 +15,6 @@ export function slugify(text) {
|
|
|
15
15
|
.replace(/-+$/, ''); // Trim - from end of text
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function fixRefs(obj, basePath) {
|
|
19
|
-
for (const key in obj) {
|
|
20
|
-
if (
|
|
21
|
-
key === '$ref' &&
|
|
22
|
-
typeof obj[key] === 'string' &&
|
|
23
|
-
!obj[key].startsWith('#') &&
|
|
24
|
-
!obj[key].startsWith('http')
|
|
25
|
-
) {
|
|
26
|
-
obj[key] = path.resolve(basePath, obj[key]);
|
|
27
|
-
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
28
|
-
fixRefs(obj[key], basePath);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
18
|
export async function processOneOfSchema(schema, filePath) {
|
|
34
19
|
const processedSchemas = [];
|
|
35
20
|
const choiceType = schema.oneOf ? 'oneOf' : null;
|
|
@@ -56,6 +41,8 @@ export async function processOneOfSchema(schema, filePath) {
|
|
|
56
41
|
const hadId = resolvedOption.$id && resolvedOption.$id.endsWith('.json');
|
|
57
42
|
if (hadId) {
|
|
58
43
|
slug = path.basename(resolvedOption.$id, '.json');
|
|
44
|
+
} else if (resolvedOption.$anchor) {
|
|
45
|
+
slug = resolvedOption.$anchor;
|
|
59
46
|
} else {
|
|
60
47
|
slug = slugify(newSchema.title);
|
|
61
48
|
}
|
|
@@ -1,99 +1,71 @@
|
|
|
1
1
|
import buildExampleFromSchema from './buildExampleFromSchema';
|
|
2
|
+
import mergeJsonSchema from 'json-schema-merge-allof';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @param {object} rootSchema The complete schema for the event.
|
|
8
|
-
* @returns {object[]} An array of group objects.
|
|
9
|
-
*/
|
|
10
|
-
export function schemaToExamples(rootSchema) {
|
|
11
|
-
const choicePoints = [];
|
|
4
|
+
const findChoicePoints = (subSchema, path = []) => {
|
|
5
|
+
if (!subSchema) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
const choiceType = subSchema.oneOf
|
|
10
|
+
? 'oneOf'
|
|
11
|
+
: subSchema.anyOf
|
|
12
|
+
? 'anyOf'
|
|
13
|
+
: null;
|
|
14
|
+
const currentChoice = choiceType ? [{ path, schema: subSchema }] : [];
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
const nestedChoices = subSchema.properties
|
|
17
|
+
? Object.entries(subSchema.properties).flatMap(([key, propSchema]) =>
|
|
18
|
+
findChoicePoints(propSchema, [...path, 'properties', key]),
|
|
19
|
+
)
|
|
20
|
+
: [];
|
|
21
|
+
|
|
22
|
+
return [...currentChoice, ...nestedChoices];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const generateExampleForChoice = (rootSchema, path, option) => {
|
|
26
|
+
const schemaVariant = JSON.parse(JSON.stringify(rootSchema));
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
if (path.length === 0) {
|
|
29
|
+
delete schemaVariant.oneOf;
|
|
30
|
+
delete schemaVariant.anyOf;
|
|
31
|
+
const newSchemaVariant = mergeJsonSchema({
|
|
32
|
+
allOf: [schemaVariant, option],
|
|
33
|
+
});
|
|
34
|
+
return buildExampleFromSchema(newSchemaVariant);
|
|
35
|
+
} else {
|
|
36
|
+
let parentOfChoice = schemaVariant;
|
|
37
|
+
for (let i = 0; i < path.length; i++) {
|
|
38
|
+
parentOfChoice = parentOfChoice[path[i]];
|
|
32
39
|
}
|
|
40
|
+
delete parentOfChoice.oneOf;
|
|
41
|
+
delete parentOfChoice.anyOf;
|
|
42
|
+
Object.assign(parentOfChoice, option);
|
|
43
|
+
return buildExampleFromSchema(schemaVariant);
|
|
33
44
|
}
|
|
45
|
+
};
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
export function schemaToExamples(rootSchema) {
|
|
48
|
+
const choicePoints = findChoicePoints(rootSchema);
|
|
36
49
|
|
|
37
|
-
// 2. If no choices are found, generate a single default example
|
|
38
50
|
if (choicePoints.length === 0) {
|
|
39
51
|
const example = buildExampleFromSchema(rootSchema);
|
|
40
52
|
if (example && Object.keys(example).length > 0) {
|
|
41
|
-
// Return in the same group structure for consistency
|
|
42
53
|
return [
|
|
43
|
-
{
|
|
44
|
-
property: 'default',
|
|
45
|
-
options: [{ title: 'Example', example }],
|
|
46
|
-
},
|
|
54
|
+
{ property: 'default', options: [{ title: 'Example', example }] },
|
|
47
55
|
];
|
|
48
56
|
}
|
|
49
57
|
return [];
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
// 3. Map each found choice point to a "group" of examples
|
|
53
60
|
return choicePoints.map(({ path, schema }) => {
|
|
54
61
|
const choiceType = schema.oneOf ? 'oneOf' : 'anyOf';
|
|
55
|
-
const choices = schema[choiceType];
|
|
56
62
|
const propertyName = path.length > 0 ? path[path.length - 1] : 'root';
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// If path is empty, the choice is at the root of the schema.
|
|
64
|
-
if (path.length === 0) {
|
|
65
|
-
// Merge the chosen option's properties into the root properties
|
|
66
|
-
schemaVariant.properties = {
|
|
67
|
-
...schemaVariant.properties,
|
|
68
|
-
...option.properties,
|
|
69
|
-
};
|
|
70
|
-
// Remove the choice block from the root
|
|
71
|
-
delete schemaVariant.oneOf;
|
|
72
|
-
delete schemaVariant.anyOf;
|
|
73
|
-
} else {
|
|
74
|
-
// The choice is nested. Find the parent of the choice block in the copied schema.
|
|
75
|
-
let parentOfChoice = schemaVariant;
|
|
76
|
-
for (let i = 0; i < path.length; i++) {
|
|
77
|
-
parentOfChoice = parentOfChoice[path[i]];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Delete the choice keywords and merge the selected option.
|
|
81
|
-
// This preserves other properties on the parent (like `type` and `description`).
|
|
82
|
-
delete parentOfChoice.oneOf;
|
|
83
|
-
delete parentOfChoice.anyOf;
|
|
84
|
-
Object.assign(parentOfChoice, option);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const example = buildExampleFromSchema(schemaVariant);
|
|
88
|
-
return {
|
|
89
|
-
title: option.title || 'Option',
|
|
90
|
-
example,
|
|
91
|
-
};
|
|
92
|
-
});
|
|
64
|
+
const options = schema[choiceType].map((option) => ({
|
|
65
|
+
title: option.title || 'Option',
|
|
66
|
+
example: generateExampleForChoice(rootSchema, path, option),
|
|
67
|
+
}));
|
|
93
68
|
|
|
94
|
-
return {
|
|
95
|
-
property: propertyName,
|
|
96
|
-
options,
|
|
97
|
-
};
|
|
69
|
+
return { property: propertyName, options };
|
|
98
70
|
});
|
|
99
71
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getConstraints } from './getConstraints';
|
|
2
|
+
import { getExamples } from './example-helper';
|
|
2
3
|
|
|
3
4
|
function processOptions(
|
|
4
5
|
choices,
|
|
@@ -36,7 +37,7 @@ function processOptions(
|
|
|
36
37
|
required: isRequired,
|
|
37
38
|
propertyType: optionSchema.type,
|
|
38
39
|
description: optionSchema.description,
|
|
39
|
-
|
|
40
|
+
examples: getExamples(optionSchema),
|
|
40
41
|
constraints: constraints,
|
|
41
42
|
isLastInGroup: isLastOption, // Updated: Uses the calculated flag instead of always true
|
|
42
43
|
hasChildren: false,
|
|
@@ -201,7 +202,7 @@ export function schemaToTableData(
|
|
|
201
202
|
propertyType:
|
|
202
203
|
propSchema.type || (propSchema.enum ? 'enum' : 'object'),
|
|
203
204
|
description: propSchema.description,
|
|
204
|
-
|
|
205
|
+
examples: getExamples(propSchema),
|
|
205
206
|
constraints,
|
|
206
207
|
isLastInGroup: isLast,
|
|
207
208
|
hasChildren,
|
|
@@ -296,7 +297,7 @@ export function schemaToTableData(
|
|
|
296
297
|
required: false,
|
|
297
298
|
propertyType: subSchema.type,
|
|
298
299
|
description: subSchema.description,
|
|
299
|
-
|
|
300
|
+
examples: getExamples(subSchema),
|
|
300
301
|
constraints: getConstraints(subSchema),
|
|
301
302
|
isLastInGroup: true,
|
|
302
303
|
hasChildren: false,
|