docusaurus-plugin-generate-schema-docs 1.8.3 → 1.8.5
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 +12 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +6 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +15 -0
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +6 -0
- package/__tests__/components/PropertiesTable.test.js +66 -0
- package/__tests__/components/PropertyRow.test.js +85 -4
- package/__tests__/components/SchemaJsonViewer.test.js +118 -0
- package/__tests__/generateEventDocs.anchor.test.js +1 -1
- package/__tests__/generateEventDocs.nested.test.js +1 -1
- package/__tests__/generateEventDocs.partials.test.js +1 -1
- package/__tests__/generateEventDocs.test.js +506 -1
- package/__tests__/generateEventDocs.versioned.test.js +1 -1
- package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
- package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
- package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
- package/__tests__/helpers/example-helper.test.js +12 -0
- package/__tests__/helpers/exampleModel.test.js +209 -0
- package/__tests__/helpers/file-system.test.js +73 -1
- package/__tests__/helpers/getConstraints.test.js +43 -0
- package/__tests__/helpers/mergeSchema.test.js +94 -0
- package/__tests__/helpers/processSchema.test.js +309 -1
- package/__tests__/helpers/schema-doc-template.test.js +54 -0
- package/__tests__/helpers/schema-processing.test.js +122 -2
- package/__tests__/helpers/schemaToExamples.test.js +1007 -0
- package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
- package/__tests__/helpers/schemaToTableData.test.js +157 -0
- package/__tests__/helpers/schemaTraversal.test.js +110 -0
- package/__tests__/helpers/snippetTargets.test.js +432 -0
- package/__tests__/helpers/trackingTargets.test.js +319 -0
- package/__tests__/helpers/validator.test.js +385 -1
- package/__tests__/index.test.js +436 -0
- package/__tests__/syncGtm.test.js +366 -6
- package/__tests__/update-schema-ids.test.js +70 -1
- package/__tests__/validateSchemas-integration.test.js +2 -2
- package/__tests__/validateSchemas.test.js +192 -1
- package/components/PropertiesTable.js +32 -2
- package/components/PropertyRow.js +29 -2
- package/components/SchemaJsonViewer.js +234 -131
- package/components/SchemaRows.css +40 -0
- package/components/SchemaViewer.js +11 -2
- package/generateEventDocs.js +21 -1
- package/helpers/constraintSchemaPaths.js +10 -14
- package/helpers/example-helper.js +2 -2
- package/helpers/getConstraints.js +20 -0
- package/helpers/processSchema.js +32 -1
- package/helpers/schema-doc-template.js +4 -0
- package/helpers/schemaToExamples.js +29 -35
- package/helpers/schemaToTableData.js +538 -492
- package/helpers/schemaTraversal.cjs +148 -0
- package/helpers/trackingTargets.js +26 -3
- package/helpers/validator.js +18 -4
- package/index.js +1 -2
- package/package.json +1 -1
- package/scripts/sync-gtm.js +65 -34
- package/test-data/payloadContracts.js +35 -0
- package/validateSchemas.js +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @jest-environment node
|
|
2
|
+
* @jest-environment @stryker-mutator/jest-runner/jest-env/node
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import path from 'path';
|
|
@@ -102,4 +102,195 @@ describe('validateSchemas', () => {
|
|
|
102
102
|
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
103
103
|
expect(consoleErrorSpy.mock.calls[0][0]).toContain('x-tracking-targets');
|
|
104
104
|
});
|
|
105
|
+
|
|
106
|
+
it('should treat falsy scalar examples as valid examples', async () => {
|
|
107
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
108
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
109
|
+
fs.writeFileSync(
|
|
110
|
+
path.join(schemaDir, 'event.json'),
|
|
111
|
+
JSON.stringify({
|
|
112
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
event: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
const: 'test_event',
|
|
118
|
+
},
|
|
119
|
+
is_enabled: {
|
|
120
|
+
type: 'boolean',
|
|
121
|
+
example: false,
|
|
122
|
+
},
|
|
123
|
+
retry_count: {
|
|
124
|
+
type: 'integer',
|
|
125
|
+
example: 0,
|
|
126
|
+
},
|
|
127
|
+
note: {
|
|
128
|
+
type: 'string',
|
|
129
|
+
example: '',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
required: ['event', 'is_enabled', 'retry_count', 'note'],
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const result = await validateSchemas(schemaDir);
|
|
137
|
+
expect(result).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should allow a top-level falsy example value', async () => {
|
|
141
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
142
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
143
|
+
fs.writeFileSync(
|
|
144
|
+
path.join(schemaDir, 'flag.json'),
|
|
145
|
+
JSON.stringify({
|
|
146
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
147
|
+
type: 'boolean',
|
|
148
|
+
example: false,
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const result = await validateSchemas(schemaDir);
|
|
153
|
+
expect(result).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should return false and log errors when a schema produces no examples', async () => {
|
|
157
|
+
const fixturePath = path.resolve(
|
|
158
|
+
__dirname,
|
|
159
|
+
'__fixtures__',
|
|
160
|
+
'validateSchemas',
|
|
161
|
+
'no-example-schema.json',
|
|
162
|
+
);
|
|
163
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
164
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
165
|
+
fs.copyFileSync(
|
|
166
|
+
fixturePath,
|
|
167
|
+
path.join(schemaDir, 'no-example-schema.json'),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const result = await validateSchemas(schemaDir);
|
|
171
|
+
expect(result).toBe(false);
|
|
172
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
173
|
+
const errorMessages = consoleErrorSpy.mock.calls
|
|
174
|
+
.map((c) => c[0])
|
|
175
|
+
.join('\n');
|
|
176
|
+
expect(errorMessages).toContain('does not produce any examples');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should return false and log error when processing a schema throws', async () => {
|
|
180
|
+
const fixturePath = path.resolve(
|
|
181
|
+
__dirname,
|
|
182
|
+
'__fixtures__',
|
|
183
|
+
'validateSchemas',
|
|
184
|
+
'main-schema-with-missing-ref.json',
|
|
185
|
+
);
|
|
186
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
187
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
188
|
+
fs.copyFileSync(
|
|
189
|
+
fixturePath,
|
|
190
|
+
path.join(schemaDir, 'main-schema-with-missing-ref.json'),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const result = await validateSchemas(schemaDir);
|
|
194
|
+
expect(result).toBe(false);
|
|
195
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
196
|
+
const errorMessages = consoleErrorSpy.mock.calls
|
|
197
|
+
.map((c) => c[0])
|
|
198
|
+
.join('\n');
|
|
199
|
+
expect(errorMessages).toContain('Error processing');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should report errors for all failing schemas when multiple schemas exist', async () => {
|
|
203
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
204
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
205
|
+
|
|
206
|
+
// First schema: valid
|
|
207
|
+
fs.writeFileSync(
|
|
208
|
+
path.join(schemaDir, 'valid.json'),
|
|
209
|
+
JSON.stringify({
|
|
210
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
211
|
+
type: 'object',
|
|
212
|
+
properties: { event: { type: 'string', const: 'ok' } },
|
|
213
|
+
required: ['event'],
|
|
214
|
+
}),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// Second schema: no examples produced
|
|
218
|
+
fs.writeFileSync(
|
|
219
|
+
path.join(schemaDir, 'no-examples.json'),
|
|
220
|
+
JSON.stringify({
|
|
221
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: { untyped: { description: 'no type or example' } },
|
|
224
|
+
required: ['untyped'],
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const result = await validateSchemas(schemaDir);
|
|
229
|
+
expect(result).toBe(false);
|
|
230
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should return false when a oneOf option produces an undefined example', async () => {
|
|
234
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
235
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
236
|
+
|
|
237
|
+
// Schema with a oneOf where one option has no resolvable example
|
|
238
|
+
fs.writeFileSync(
|
|
239
|
+
path.join(schemaDir, 'undef-option.json'),
|
|
240
|
+
JSON.stringify({
|
|
241
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
242
|
+
type: 'object',
|
|
243
|
+
oneOf: [
|
|
244
|
+
{
|
|
245
|
+
title: 'ValidOption',
|
|
246
|
+
properties: { event: { type: 'string', const: 'test' } },
|
|
247
|
+
required: ['event'],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
title: 'UndefinedOption',
|
|
251
|
+
properties: { mystery: { description: 'no type, no example' } },
|
|
252
|
+
required: ['mystery'],
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
}),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const result = await validateSchemas(schemaDir);
|
|
259
|
+
expect(result).toBe(false);
|
|
260
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
261
|
+
const errorMessages = consoleErrorSpy.mock.calls
|
|
262
|
+
.map((c) => c[0])
|
|
263
|
+
.join('\n');
|
|
264
|
+
expect(errorMessages).toContain('does not produce a valid example');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should report all examples invalid when every example fails validation', async () => {
|
|
268
|
+
const schemaDir = path.join(tmpDir, 'schemas');
|
|
269
|
+
fs.mkdirSync(schemaDir, { recursive: true });
|
|
270
|
+
|
|
271
|
+
// Schema where every oneOf option produces an example that fails schema validation
|
|
272
|
+
fs.writeFileSync(
|
|
273
|
+
path.join(schemaDir, 'all-invalid.json'),
|
|
274
|
+
JSON.stringify({
|
|
275
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
276
|
+
type: 'object',
|
|
277
|
+
properties: {
|
|
278
|
+
count: {
|
|
279
|
+
type: 'integer',
|
|
280
|
+
minimum: 100,
|
|
281
|
+
example: 1,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
required: ['count'],
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const result = await validateSchemas(schemaDir);
|
|
289
|
+
expect(result).toBe(false);
|
|
290
|
+
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
291
|
+
const errorMessages = consoleErrorSpy.mock.calls
|
|
292
|
+
.map((c) => c[0])
|
|
293
|
+
.join('\n');
|
|
294
|
+
expect(errorMessages).toContain('example data failed validation');
|
|
295
|
+
});
|
|
105
296
|
});
|
|
@@ -5,9 +5,39 @@ import WordWrapButton from './WordWrapButton';
|
|
|
5
5
|
import { schemaToTableData } from '../helpers/schemaToTableData';
|
|
6
6
|
import styles from './PropertiesTable.module.css';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function filterInheritedTopLevelProperties(schema, sourceSchema) {
|
|
9
|
+
if (!schema?.properties || !sourceSchema?.properties) {
|
|
10
|
+
return schema;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sourceKeys = new Set(Object.keys(sourceSchema.properties));
|
|
14
|
+
const filteredEntries = Object.entries(schema.properties).filter(
|
|
15
|
+
([key, propSchema]) => {
|
|
16
|
+
if (sourceKeys.has(key)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const hasDescription =
|
|
21
|
+
typeof propSchema?.description === 'string' &&
|
|
22
|
+
propSchema.description.trim().length > 0;
|
|
23
|
+
const hasExamples =
|
|
24
|
+
Array.isArray(propSchema?.examples) && propSchema.examples.length > 0;
|
|
25
|
+
const hasExample = propSchema?.example !== undefined;
|
|
26
|
+
|
|
27
|
+
return hasDescription || hasExamples || hasExample;
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
...schema,
|
|
33
|
+
properties: Object.fromEntries(filteredEntries),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function PropertiesTable({ schema, sourceSchema }) {
|
|
9
38
|
const [isWordWrapOn, setIsWordWrapOn] = useState(true);
|
|
10
|
-
const
|
|
39
|
+
const tableSchema = filterInheritedTopLevelProperties(schema, sourceSchema);
|
|
40
|
+
const tableData = schemaToTableData(tableSchema);
|
|
11
41
|
const stripeState = { current: 0 };
|
|
12
42
|
|
|
13
43
|
return (
|
|
@@ -26,6 +26,19 @@ const getContainerSymbol = (containerType) => {
|
|
|
26
26
|
return '';
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
const formatPropertyType = (value) => {
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
return value.join(', ');
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === 'string') {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
if (value === undefined || value === null) {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify(value);
|
|
40
|
+
};
|
|
41
|
+
|
|
29
42
|
const KEYWORD_HELP_TEXT = {
|
|
30
43
|
additionalProperties:
|
|
31
44
|
'Controls properties not listed in properties and not matched by patternProperties.',
|
|
@@ -33,6 +46,8 @@ const KEYWORD_HELP_TEXT = {
|
|
|
33
46
|
'Applies the subschema to property names that match the given regular expression.',
|
|
34
47
|
};
|
|
35
48
|
|
|
49
|
+
const SCHEMA_KEYWORD_BADGE_TEXT = 'Schema constraint';
|
|
50
|
+
|
|
36
51
|
function splitKeywordLabel(name) {
|
|
37
52
|
const match = /^patternProperties (\/.+\/)$/.exec(name);
|
|
38
53
|
if (!match) {
|
|
@@ -45,6 +60,15 @@ function splitKeywordLabel(name) {
|
|
|
45
60
|
};
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
function buildKeywordHelpId(name, rowPath) {
|
|
64
|
+
if (!rowPath || rowPath.length === 0) {
|
|
65
|
+
return `schema-keyword-help-${name}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const normalizedPath = rowPath.join('-').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
69
|
+
return `schema-keyword-help-${normalizedPath}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
48
72
|
/**
|
|
49
73
|
* Renders a single property row in the schema table.
|
|
50
74
|
* All data is passed in via the `row` prop, which comes from `tableData`.
|
|
@@ -145,7 +169,7 @@ export default function PropertyRow({
|
|
|
145
169
|
: name;
|
|
146
170
|
const keywordHelpText = KEYWORD_HELP_TEXT[keywordHelpKey];
|
|
147
171
|
const keywordHelpId = keywordHelpText
|
|
148
|
-
?
|
|
172
|
+
? buildKeywordHelpId(name, row.path)
|
|
149
173
|
: undefined;
|
|
150
174
|
const zebraClassName =
|
|
151
175
|
stripeIndex === undefined
|
|
@@ -208,6 +232,9 @@ export default function PropertyRow({
|
|
|
208
232
|
{name}
|
|
209
233
|
</code>
|
|
210
234
|
)}
|
|
235
|
+
<span className="property-keyword-badge">
|
|
236
|
+
{SCHEMA_KEYWORD_BADGE_TEXT}
|
|
237
|
+
</span>
|
|
211
238
|
<span
|
|
212
239
|
id={keywordHelpId}
|
|
213
240
|
className="property-keyword-tooltip"
|
|
@@ -222,7 +249,7 @@ export default function PropertyRow({
|
|
|
222
249
|
</span>
|
|
223
250
|
</td>
|
|
224
251
|
<td rowSpan={rowSpan}>
|
|
225
|
-
<code>{propertyType}</code>
|
|
252
|
+
<code>{formatPropertyType(propertyType)}</code>
|
|
226
253
|
</td>
|
|
227
254
|
|
|
228
255
|
{/* The first constraint cell */}
|